Adicionar imagens ao app Android

1. Antes de começar

Neste codelab, você vai aprender a adicionar imagens ao seu app usando um elemento combinável Image.

Pré-requisitos

  • Conhecimento básico sobre como criar e executar um app no Android Studio.
  • Conhecimento básico sobre como adicionar elementos da IU. Por exemplo, elementos de texto que podem ser compostos.

O que você vai aprender

  • Como adicionar uma imagem ou foto ao seu app Android.
  • Como mostrar uma imagem no seu app com um elemento Image combinável.
  • Práticas recomendadas sobre o uso de recursos String.

O que você vai criar

  • Melhorar o app Happy Birthday para incluir uma imagem.

Pré-requisitos

2. Configurar o app

No Android Studio, abra seu projeto Happy Birthday do codelab anterior.

Quando o app for executado, ele vai ficar como o mostrado nesta captura de tela.

fdf3fc55ab1d348a.png

Adicionar uma imagem ao projeto

Nesta tarefa, você vai fazer o download de uma imagem da Internet e a adicionar ao app Happy Birthday.

  1. Abra a imagem do seu app de cartão de aniversário usando este link.
  2. Clique em Fazer o download.

1d731e32164fca8a.png

  1. Clique na imagem com o botão direito do mouse e salve o arquivo no computador como androidparty.png.
  2. Anote onde você salvou a imagem.

Por exemplo, você pode ter usado a pasta Downloads.

  1. No Android Studio, clique em View > Tool Windows > Resource Manager ou na guia Resource Manager ao lado da janela Project.

3cda2692405276d.png

3b6c04c505468229.png

  1. Clique em + (Add resources to the module) > Import Drawables.

a85b707150fcff9a.png

  1. No navegador de arquivos, selecione o arquivo de imagem que você transferiu por download e clique em Open.

Essa ação abre a caixa de diálogo Import drawables.

d64abadd7b79ecf0.png

  1. O Android Studio vai mostrar uma visualização da imagem. Selecione Density na lista suspensa QUALIFIER TYPE. Você vai descobrir por que está fazendo isso em uma sessão posterior.

o tipo de qualificador está sendo definido como "densidade"

  1. Selecione No Density na lista VALUE.

 nenhum dpi selecionado na lista suspensa de valor

Os dispositivos Android têm telas de tamanhos variados (por exemplo, smartphones, tablets, TVs etc.), e elas também têm diferentes tamanhos de pixel. Ou seja, enquanto um dispositivo tem 160 pixels por polegada quadrada, outro encaixa 480 pixels no mesmo espaço. Caso você não considere essas variações em densidade de pixel, o sistema pode dimensionar suas imagens, resultando em imagens desfocadas, imagens grandes que consomem muita memória ou imagens com tamanho incorreto.

Quando você redimensiona imagens que ultrapassam o tamanho que o sistema Android consegue processar, é gerado um erro de falta de memória. As fotografias e imagens de plano de fundo, como a atual (androidparty.png), precisam ser colocadas na pasta drawable-nodpi, o que interrompe o comportamento de redimensionamento.

Para ver mais informações sobre densidades de pixel, consulte Suporte a densidades de pixel diferentes.

  1. Clique em Próximo.
  2. O Android Studio mostra a estrutura de pastas em que a imagem será colocada. Observe a pasta drawable-nodpi.
  3. Clique em Import(C).

74d23f98b8018f93.png

O Android Studio cria uma pasta drawable-nodpi e coloca a imagem nela. Na visualização de projeto do Android Studio, o nome do recurso é mostrado como androidparty.png (nodpi). No sistema de arquivos do computador, o Android Studio criaria uma pasta com o nome drawable-nodpi.

5e5ca441e391929e.png

Ele é colocado no drawable de pasta sem dpi.

Caso a imagem seja importada corretamente, ela será adicionada pelo Android Studio à lista na guia Drawable. Essa lista inclui todos os ícones e imagens do app, então você já pode usar a imagem importada.

mostra a imagem recém-adicionada

  1. Volte à visualização do projeto, clique em View > Tool Windows > Project ou clique na guia Project à esquerda.
  2. Clique em app > res > drawable para confirmar que a imagem está na pasta drawable.

f95964d9f0ee2dba.png

3. Adicionar uma imagem que pode ser composta

Para que uma imagem seja mostrada no app, é necessário informar o local de exibição. Do mesmo modo como você usa um elemento combinável Text para mostrar texto, é possível usar um elemento Image para mostrar uma imagem.

Nesta tarefa, você vai adicionar um elemento combinável Image ao app, usar a imagem que foi transferida por download, posicionar essa imagem e ajustar o tamanho dela para preencher a tela.

Incluir uma função combinável para adicionar uma imagem

  1. No arquivo MainActivity.kt, adicione uma função combinável GreetingImage() após a função GreetingText().
  2. Transmita dois parâmetros String à função GreetingImage(): um com o nome message para a mensagem de aniversário e outro com o nome from para sua assinatura.
@Composable
fun GreetingImage(message: String, from: String) {
}
  1. Todas as funções combináveis precisam aceitar um parâmetro Modifier opcional. Os modificadores informam para um elemento da interface como serão dispostos, exibidos ou se comportarão no layout pai. Adicione outro parâmetro ao elemento combinável GreetingImage().
@Composable
fun GreetingImage(message: String, from: String, modifier: Modifier = Modifier) {
}

Recursos no Jetpack Compose

Os recursos são os arquivos complementares e o conteúdo estático usados pelo seu código, como bitmaps, strings da interface do usuário, instruções de animação, entre outros. Para saber mais sobre recursos no Android, consulte Visão geral dos recursos de app.

É importante sempre separar do código os recursos do app, como imagens e strings, para que eles possam ser mantidos de forma independente. No momento da execução, o Android usa o recurso adequado com base na configuração atual. Por exemplo, você pode querer fornecer um layout de IU diferente de acordo com o tamanho da tela, ou strings diferentes de acordo com a configuração de idioma.

Como agrupar recursos

Coloque cada tipo de recurso em um subdiretório específico do diretório res/ do seu projeto. Por exemplo, abaixo mostramos a hierarquia de arquivos para um projeto simples:

MyProject/
    src/
        MyActivity.kt
    res/
        drawable/
            graphic.png
        mipmap/
            icon.png
        values/
            strings.xml

Como você pode ver nesse exemplo, o diretório res/ contém todos os recursos em subdiretórios, incluindo um diretório drawable/ para um recurso de imagem, um mipmap/ para ícones na tela de início e um values/ para recursos de string. Se quiser saber mais sobre o uso, o formato e a sintaxe dos recursos do app, consulte a Visão geral dos tipos de recurso.

Como acessar recursos

O Jetpack Compose pode acessar os recursos definidos no seu projeto Android. O acesso pode ser feito usando IDs de recurso que são gerados na classe R do projeto.

A classe R é gerada automaticamente pelo Android e contém os IDs de todos os recursos no projeto. Na maioria dos casos, o ID do recurso é igual ao nome do arquivo. Por exemplo, a imagem na hierarquia de arquivos anterior pode ser acessada com este código:

R.drawable.graphic

"R" é uma classe gerada automaticamente, "drawable" é um subdiretório na pasta "res" e "graphic" é o ID do recurso

Na próxima tarefa, você vai usar o arquivo de imagem androidparty.png, que foi adicionado na tarefa anterior.

  1. Na função GreetingImage(), declare uma propriedade val e a nomeie como image.
  2. Faça uma chamada para a função painterResource() transmitindo o recurso androidparty. Atribua o valor retornado à variável image.
val image = painterResource(R.drawable.androidparty)

O Android Studio destaca o código painterResource porque é necessário importar a função para compilar o app.

82323978341a0bdc.png

  1. Clique no painterResource, que é destacado pelo Android Studio.
  2. Clique em Import no pop-up para adicionar a importação de androidx.compose.ui.res.painterResource.

A função painterResource() carrega um recurso de imagem drawable e usa o ID do recurso (R.drawable.androidparty, neste caso) como um argumento.

  1. Após a chamada para a função painterResource(), adicione um elemento Image combinável e, em seguida, transmita o image para o painter como um argumento nomeado.
Image(
    painter = image
)

O Android Studio destaca o código Image porque é necessário importar a função para compilar o app.

e2bead11643e0577.png

Para corrigir esse aviso, adicione a importação abaixo na parte de cima do arquivo MainActivity.kt:

import androidx.compose.foundation.Image

O aviso inicial foi resolvido, mas se você passar o cursor sobre a palavra Image, o Android Studio vai mostrar um novo aviso informando que nenhuma destas funções pode ser chamada com os argumentos fornecidos. Isso ocorre porque o argumento fornecido não corresponde a nenhuma das assinaturas da função Image.

ad35f0a40f8fc849.png

Esse aviso será corrigido na próxima seção.

Conferir a acessibilidade do app

Ao seguir as práticas de programação para acessibilidade, você permite que todos os seus usuários, incluindo aqueles com deficiências, naveguem com mais facilidade no seu app e interajam com ele.

O Android Studio apresenta dicas e alertas para ajudar você a tornar seu app mais acessível. O uso de uma descrição de conteúdo define a finalidade de um elemento da IU, o que faz com que seu app seja mais utilizável com o TalkBack.

Contudo, a imagem neste app só foi incluída para fins decorativos. A adição de uma descrição de conteúdo da imagem dificultaria o uso com o TalkBack neste caso específico. Em vez de definir a descrição de conteúdo que é anunciada ao usuário, você pode definir o argumento contentDescription da imagem como null para que o TalkBack ignore o elemento Image.

  • No elemento Image, adicione outro argumento nomeado, o contentDescription, e defina o valor dele como null.
Image(
    painter = image,
    contentDescription = null
)

Visualizar o elemento Image combinável

Nesta tarefa, você vai visualizar a imagem combinável e executar o app em um emulador ou dispositivo.

  1. Na função BirthdayCardPreview(), substitua a chamada de função GreetingText() por uma chamada GreetingImage().

A função vai ser semelhante a este snippet de código:

@Preview(showBackground = true)
@Composable
fun BirthdayCardPreview() {
    HappyBirthdayTheme {
        GreetingImage(
            message = "Happy Birthday Sam!",
            from = "From Emma"
        )
    }
}
  1. O painel Design será atualizado automaticamente. Caso contrário, clique em 609ccb451d05cf6b.png para criar.

Não é mais possível encontrar o texto, porque a nova função tem apenas um elemento Image, mas não tem um Text.

acd47e25eb2a8d55.png

4. Adicionar layout de caixa

Os três elementos básicos de layout padrão do Compose são Column, Row e Box. Você aprendeu sobre os combináveis Column e Row nos codelabs anteriores. Agora, vamos conhecer melhor o elemento combinável Box.

O layout Box é um dos elementos de layout padrão do Compose. Use o layout Box para empilhar elementos uns sobre os outros. O layout Box também permite que você configure o alinhamento específico dos elementos que ele contém.

4d191637aaecf374.png

  1. Na função GreetingImage(), adicione um elemento Box ao redor do elemento Image, conforme mostrado abaixo:
@Composable
fun GreetingImage(message: String, from: String, modifier: Modifier = Modifier) {
    val image = painterResource(R.drawable.androidparty)
    Box {
        Image(
            painter = image,
            contentDescription = null
        )
    }
}
  1. Importe a função androidx.compose.foundation.layout.Box quando solicitado pelo Android Studio.
  2. Adicione o código para transmitir o parâmetro modifier ao elemento combinável Box.
@Composable
fun GreetingImage(message: String, from: String, modifier: Modifier = Modifier) {
    val image = painterResource(R.drawable.androidparty)
    Box(modifier) {
        Image(
            painter = image,
            contentDescription = null
        )
    }
}
  1. Ao final do elemento combinável Box, chame a função GreetingText() e transmita a ela a mensagem de aniversário, a assinatura e o modificador, conforme mostrado abaixo:
@Composable
fun GreetingImage(message: String, from: String, modifier: Modifier = Modifier) {
    val image = painterResource(R.drawable.androidparty)
    Box(modifier) {
        Image(
            painter = image,
            contentDescription = null
        )
        GreetingText(
            message = message,
            from = from,
            modifier = Modifier
                .fillMaxSize()
                .padding(8.dp)
        )
    }
}
  1. Observe a prévia atualizada no painel Design.

O texto e a imagem vão aparecer.

A imagem de plano de fundo está fixada na parte de cima.

  1. Para que as mudanças acima sejam aplicadas no emulador ou em um dispositivo, na função onCreate(), substitua a chamada de função GreetingText() por GreetingImage().

O bloco setContent será semelhante a este snippet de código:

setContent {
    HappyBirthdayTheme {
        // A surface container using the 'background' color from the theme
        Surface(
            modifier = Modifier.fillMaxSize(),
            color = MaterialTheme.colorScheme.background
        ) {
            GreetingImage(
                message = "Happy Birthday Sam!",
                from = "From Emma"
            )
        }
    }
}

A imagem tem a mesma largura da tela, mas está fixada na parte de cima. Há um espaço em branco na parte de baixo da tela, o que não é muito interessante. Na próxima tarefa, você vai preencher a largura e altura da tela e dimensionar a imagem.

5. Mudar a opacidade e dimensionar a imagem

Nesta tarefa, você vai colocar a imagem em tela cheia para deixar seu app mais bonito. Para fazer isso, use os parâmetros ContentScale.

Dimensionar conteúdo

Você adicionou a imagem ao seu app e a posicionou. Agora, é necessário ajustar o tipo de escala da imagem, que informa como dimensionar para que ela seja mostrada em tela cheia.

Há alguns tipos de ContentScale disponíveis. Use o escalonamento de parâmetros ContentScale.Crop, que dimensiona a imagem de maneira uniforme para manter a proporção de modo que a largura e a altura dela sejam iguais ou maiores que a dimensão correspondente da tela.

  1. Adicione um argumento nomeado ContentScale à imagem.
Image(
    painter = image,
    contentDescription = null,
    contentScale = ContentScale.Crop
)
  1. Importe a propriedade androidx.compose.ui.layout.ContentScale quando solicitado pelo Android Studio.
  2. Confira o painel Design.

A imagem agora preenche toda a tela de prévia, como mostra esta captura de tela:

ae1a5ec6b294f466.png

Mudar a opacidade

Para melhorar o contraste do app, mude a opacidade da imagem de plano de fundo.

Adicione o parâmetro alpha ao elemento combinável Image e defina-o como 0.5F.

Image(
    painter = image,
    contentDescription = null,
    contentScale = ContentScale.Crop,
    alpha = 0.5F
)

Observe a mudança na opacidade da imagem.

É bastante código. Está na hora de conferir o resultado de todo o seu trabalho.

Executar o app

Execute o app em um dispositivo ou emulador.

9d1416521733e8c.png

Bom trabalho com a imagem em tela cheia e a mensagem de texto! Você também mudou a opacidade da imagem.

Modificadores de layout

Os modificadores são usados para decorar ou adicionar comportamentos aos elementos da interface do Jetpack Compose. Por exemplo, você pode adicionar planos de fundo, padding ou comportamento a linhas, textos ou botões. Para fazer a configuração deles, um elemento combinável ou layout precisa aceitar um modificador como parâmetro.

Em um codelab anterior, você aprendeu sobre os modificadores e usou o de padding (Modifier.padding) para adicionar espaço ao redor do elemento combinável Text. Os modificadores podem ajudar muito, e você verá isso neste e nos próximos programas de aprendizagem.

Por exemplo, o elemento combinável Text tem um argumento Modifier que muda a cor do plano de fundo para verde.

// Example
Text(
    text = "Hello, World!",
    // Solid element background color
    modifier = Modifier.background(color = Color.Green)
)

Assim como no exemplo acima, você pode adicionar modificadores a layouts para posicionar os elementos filhos usando propriedades de organização e alinhamento.

Para definir a posição dos filhos em uma Row, defina os argumentos horizontalArrangement e verticalAlignment. Para uma Column, defina os argumentos verticalArrangement e horizontalAlignment.

A propriedade de organização é usada para dispor os elementos filhos quando o tamanho do layout é maior que a soma dos filhos.

Por exemplo: quando o tamanho da Column é maior que a soma dos tamanhos dos filhos, uma verticalArrangement pode ser especificada para definir o posicionamento do elementos filhos na Column. Veja abaixo uma ilustração dos diferentes arranjos verticais:

mesma altura, espaçamento, espaço ao redor, espaçamento uniforme, em cima, no centro e embaixo

Da mesma forma, quando o tamanho da Row é maior que a soma dos tamanhos dos filhos, uma horizontalArrangement pode ser especificada para definir o posicionamento dos filhos dentro da Row. Verifique abaixo uma ilustração das diferentes organizações horizontais:

peso igual, espaçamento, espaço ao redor, espaçamento uniforme, fim, centro e início

A propriedade de alinhamento é usada para alinhar os elementos filhos no início, no centro ou no fim do layout.

6. Alinhar e organizar o texto

Nesta tarefa, você vai observar o código adicionado no codelab anterior para organizar o texto no app.

  1. No arquivo MainActivity.kt, role até a função GreetingText(). A propriedade verticalArrangement na coluna é definida como Arrangement.Center. O conteúdo do texto será centralizado na tela.
@Composable
fun GreetingText(message: String, from: String, modifier: Modifier = Modifier) {
    Column(
        verticalArrangement = Arrangement.Center,
        modifier = modifier
    ) {
        Text(
            text = message,
            fontSize = 100.sp,
            lineHeight = 116.sp,
            textAlign = TextAlign.Center
        )
        Text(
            text = from,
            fontSize = 36.sp,
            modifier = Modifier
                .padding(16.dp)
                .align(alignment = Alignment.End)
        )
    }
}

Padding

Um elemento da IU envolve o conteúdo. Para evitar que eles fiquem muito próximos, você pode especificar a quantidade de padding em cada lado.

Elemento combinável de texto sem padding

Elemento combinável de texto com padding

Um padding é usado como um modificador, o que significa que ele pode ser aplicado a qualquer elemento combinável. Para cada lado do elemento, o modificador padding usa um argumento opcional que define a quantidade de padding.

O diagrama mostra os paddings das partes de cima e de baixo, assim como de início e fim

// This is an example.
Modifier.padding(
    start = 16.dp,
    top = 16.dp,
    end = 16.dp,
    bottom = 16.dp
)
  1. Sua vez No arquivo MainActivity.kt, role até onde a função GreetingText() é chamada e observe o atributo de padding.
modifier = Modifier
    .fillMaxSize()
    .padding(8.dp)
  1. Da mesma forma, observe dentro da função GreetingText(), o padding do elemento combinável Text da assinatura.
modifier = Modifier
    .padding(16.dp)
    .align(alignment = Alignment.End)

7. Adotar boas práticas de código

Tradução

Ao criar apps, é importante lembrar que eles podem ser traduzidos para outro idioma em algum momento. Como você aprendeu em um codelab anterior, um tipo de dado String é uma sequência de caracteres, como "Happy Birthday Sam!".

As strings fixadas no código são escritas diretamente no código do seu app. Elas dificultam a tradução do app para outros idiomas e fazem com que seja mais difícil reutilizar strings em locais diferentes do app. Para resolver esses problemas, você pode extrair strings para um arquivo de recursos. Em vez de fixar strings no código, coloque em um arquivo, nomeie os recursos de string e utilize os nomes sempre que quiser usar as strings. O nome vai continuar igual, mesmo que a string mude ou seja traduzida para outro idioma.

  1. No arquivo MainActivity.kt, role até a função onCreate(). Selecione a mensagem de aniversário, string Happy Birthday Sam! sem aspas.
  2. Clique na lâmpada no lado esquerdo da tela.
  3. Selecione Extract string resource.

eed032464b68ee5.png

O Android Studio vai abrir a caixa de diálogo Extract Resource. Nela, é possível personalizar o nome do recurso de string e alguns detalhes de armazenamento. O campo Resource name é onde você digita o nome da string que será chamada. O campo Resource value é onde você insere a string.

  1. Na caixa de diálogo Extract Resource, mude Resource name para happy_birthday_text.

Os recursos de string precisam ter nomes em letras minúsculas, e palavras diferentes precisam ser separadas por um sublinhado. Não mude as outras configurações.

d6cb11f087b79f1e.png

  1. Clique em OK.
  2. Confira as mudanças no código.

A string fixada no código foi substituída por uma chamada para a função getString().

GreetingImage(
    message = getString(R.string.happy_birthday_text),
    from = "From Emma",
    modifier = Modifier.padding(8.dp)
)
  1. No painel Project, abra o arquivo strings.xml no caminho app > res > values > strings.xml e observe que o Android Studio criou um recurso de string chamado happy_birthday_text.
<resources>
    <string name="app_name">Happy Birthday</string>
    <string name="happy_birthday_text">Happy Birthday Sam!</string>
</resources>

O arquivo strings.xml tem uma lista de strings que aparecem para o usuário no app. Observe que o nome do app também é um recurso de string. Colocar todas as strings em um só lugar facilita a tradução de todo o texto e a reutilização de uma string em diferentes partes do app.

  1. Siga as mesmas etapas para extrair o texto do elemento Text da assinatura, mas desta vez, insira signature_text no campo Resource name.

O arquivo final será semelhante a este snippet de código:

<resources>
    <string name="app_name">Happy Birthday</string>
    <string name="happy_birthday_text">Happy Birthday Sam!</string>
    <string name="signature_text">From Emma</string>
</resources>
  1. Atualize o BirthdayCardPreview() para usar stringResource() e as strings extraídas.
@Preview(showBackground = true)
@Composable
fun BirthdayCardPreview() {
    HappyBirthdayTheme {
        GreetingImage(
            message = stringResource(R.string.happy_birthday_text),
            from = stringResource(R.string.signature_text)
        )
    }
}
  1. Execute o app novamente para conferir se ele ainda funciona.

8. Desafio

Você fez um bom trabalho adicionando a imagem ao seu app. Aqui está um desafio:

  1. Organize ou alinhe o elemento combinável de texto de assinatura para que ele fique centralizado na tela.

O teste vai ficar assim:

b681900fe13e5598.png

Para sua referência, este é o código da solução para a função GreetingText():

@Composable
fun GreetingText(message: String, from: String, modifier: Modifier = Modifier) {
    Column(
        verticalArrangement = Arrangement.Center,
        modifier = modifier
    ) {
        Text(
            text = message,
            fontSize = 100.sp,
            lineHeight = 116.sp,
            textAlign = TextAlign.Center
        )
        Text(
            text = from,
            fontSize = 36.sp,
            modifier = Modifier
                .padding(16.dp)
                .align(alignment = Alignment.CenterHorizontally)
        )
    }
}

9. Acessar o código da solução

O código da solução para o app Happy Birthday está no GitHub.

O GitHub é um serviço que possibilita que desenvolvedores gerenciem códigos de projetos de software. Ele usa o Git, um sistema de controle de versões que monitora as mudanças feitas em cada versão do código. Se você já viu o histórico de versões de um arquivo no Documentos Google, pode ver as edições e quando elas foram feitas. Da mesma forma, é possível acompanhar o histórico de versões do código em um projeto. Isso é útil quando você trabalha em um projeto individualmente ou com uma equipe.

O GitHub também tem um site em que é possível ver e gerenciar o projeto. Esse link permite procurar os arquivos do projeto Happy Birthday on-line ou fazer o download deles no seu computador.

Para fazer o download do código do codelab concluído, use este comando git:

$ git clone https://github.com/google-developer-training/basic-android-kotlin-compose-birthday-card-app.git

Se preferir, você pode fazer o download do repositório como um arquivo ZIP, descompactar e abrir no Android Studio.

Se você quiser conferir o código da solução, acesse o GitHub (link em inglês).

Ramificações no GitHub

Antes de entender o que é uma ramificação, entenda o que é um repositório. Esse termo se refere a todo o seu projeto (diretórios e arquivos) que você clona (copia) no computador. Uma ramificação é uma versão do seu repositório, ou seja, uma linha de desenvolvimento independente. Neste curso, por exemplo, a ramificação starter pode ser uma versão do seu projeto que você usou para iniciar a criação durante o codelab. A ramificação main ou solution é a versão do seu projeto ao final do codelab, contendo o código completo da solução.

Um repositório pode conter várias ramificações, o que significa que há várias versões do código nele.

10. Conclusão

Você adicionou uma imagem ao seu app Happy Birthday, alinhou o texto aos modificadores, seguiu as diretrizes de acessibilidade e facilitou a tradução para outros idiomas. E o mais importante: você terminou de criar seu app Happy Birthday. Compartilhe seu trabalho nas mídias sociais e use a hashtag #AndroidBasics para que possamos conferir o que você fez.

Resumo

  • A guia Resource Manager no Android Studio ajuda você a adicionar e organizar suas imagens e outros recursos.
  • Um elemento Image combinável é um elemento de IU que mostra imagens no app.
  • Um elemento Image combinável precisa ter uma descrição de conteúdo para deixar seu app mais acessível.
  • O texto que é mostrado ao usuário, como a mensagem de aniversário, precisa ser extraído para um recurso de string de modo a facilitar a tradução do app para outros idiomas.

Saiba mais