Firebase configuração remota para verificação de atualizações de aplicativos

Tempo de leitura: 7 minutes

Introdução

O Firebase é uma plataforma de desenvolvimento de aplicativos móveis e da web desenvolvida pela Firebase, Inc. em 2011, depois adquirida pelo Google em 2014. Em março de 2020, a plataforma Firebase tinha 19 produtos, que são usados por mais de 1,5 milhão de aplicativos.

O Firebase se tornou muito necessário para os desenvolvedores Android atualmente. Já escrevi uma postagem sobre Como usar o Configuração remota para alterar a aparência do aplicativo. Verifique-o para uma compreensão básica do Configuração remota do Firebase. Nesta postagem, aprenderemos como usar o Configuração remota para mostrar pop-ups de atualizações de aplicativos com o uso da API Rest.

 

O que são atualizações de aplicativos?

Uma atualização de aplicativo nada mais é do que uma nova versão disponível na loja, no entanto, o usuário está usando uma versão mais antiga do aplicativo. É muito comum mostrar pop-ups de atualização de aplicativos dentro do aplicativo quando uma nova versão opcional ou obrigatória de aplicativos é lançada.

Então, para entender melhor, vamos dar um exemplo, onde tenho um aplicativo na Play Store, mas houve um erro programático ou uma mudança de requisito de negócios e não quero que nenhum dos meus usuários antigos ou novos instalem ou usem a versão mais antiga do meu aplicativo. Então o que eu faço? Se eu consertar essa coisa específica e lançar uma nova versão do aplicativo na loja.

Isso é o suficiente? Não, corrigir apenas esse problema específico não é aceitável. Aqui, temos um requisito de que nenhum usuário pode usar a versão mais antiga do aplicativo. Então, o que fazemos? Aqui temos que mostrar uma atualização obrigatória. Existem dois tipos de atualizações de aplicativos que são

  • Atualização obrigatória: aqui geralmente mostramos um pop-up obrigatório para atualizar o aplicativo, a menos que não permitamos que os usuários usem o aplicativo. Isso é obrigatório, o que significa que o usuário não pode navegar ou usar qualquer recurso do aplicativo sem atualizar para a versão mais recente.

  • Última atualização: aqui geralmente mostramos o pop-up de que havia uma nova versão disponível na loja e solicitamos que ele atualize o aplicativo para melhor uso. Esta é uma atualização opcional e mostramos isso aleatoriamente, nem sempre que frustra o usuário. Aqui, o usuário pode navegar ou usar qualquer recurso do aplicativo sem atualizar para a versão mais recente

Acho que você tem uma ideia básica sobre o que vamos implementar, então vamos verificar a parte de codificação.

 

O que nós vamos fazer

Vamos fazer a parte da codificação passo a passo para facilitar o entendimento. Nosso requisito é

  • Configure o Firebase Remote Config
  • Adicione os parâmetros necessários na seção Configuração remota no Firebase console
  • Definir valores de parâmetro padrão dentro do aplicativo para o requisito de verificação de atualização
  • Adicione lógica para buscar, ativar e obter valores de parâmetro
  • Depois de buscar os valores mais recentes, compare as versões do aplicativo atual e a versão remota e mostre os pop-ups de atualização, se necessário.

Implementação

Etapa 1: se você ainda não adicionou o Firebase, adicione o Firebase ao seu projeto Android. Em seguida, vamos adicionar a dependência de configuração remota no nível do aplicativo build.gradle (app/build.gradle)

implementation 'com.google.firebase:firebase-config-ktx:19.1.4'
implementation 'com.google.firebase:firebase-analytics-ktx:17.4.3'

 

Para o Configuração remota, o Google Analytics é necessário para a segmentação condicional de instâncias do aplicativo para propriedades do usuário, públicos e Previsões do Firebase. Certifique-se de habilitar o Google Analytics em seu projeto.

Etapa 2: adicione os parâmetros necessários no Configuração remota. Agora é hora de adicionar os valores necessários no console. No console do Firebase, abra seu projeto e selecione Configuração remota (Remote Config) na seção Expandir (Grow) no menu do lado esquerdo

Os parâmetros necessários são

min_version_of_app: especifica a versão mínima necessária para usar o aplicativo. Sem esta versão ou superior, o usuário não pode acessar o conteúdo do aplicativo. Mostramos o pop-up de atualização obrigatório neste caso.

latest_version_of_app: especifica a versão mais recente do aplicativo na loja. Neste caso, mostramos o pop-up de atualização opcional.

O nome dos parâmetros pode ser qualquer coisa que você goste, mas o tema dos parâmetros é importante, por que os estamos usando e eles atendem aos nossos requisitos.

Temos que preencher “Parameter key” e “Default value” e, finalmente, não esquecer de clicar em “PUBLISH CHANGES” para que nossas alterações entrem em vigor. Adicione o parâmetro min_version_of_app e latest_version_of_app e clique no botão Publicar alterações.

Agora que terminamos do lado do servidor, vamos passar para a implementação do lado do cliente.

 

Etapa 3: defina os valores de parâmetro padrão dentro do aplicativo necessários. Podemos definir valores de parâmetro padrão dentro do aplicativo no objeto do Configuração remota para que nosso aplicativo se comporte como pretendido antes de se conectar ao back-end do Configuração remota e para que os valores padrão estejam disponíveis se nenhum for definido no back-end.

  1. Defina um conjunto de nomes de parâmetro e valores de parâmetro padrão usando um objeto de mapa (Map) ou um arquivo de recurso XML armazenado na pasta res/xml do seu aplicativo. O aplicativo de amostra de início rápido do Configuração remota usa um arquivo XML para definir nomes e valores de parâmetro padrão.
  2. Adicione esses valores ao objeto do Configuração remota usando setDefaultsAsync(int), conforme mostrado:
val remoteConfig = Firebase.remoteConfig
remoteConfig.setDefaultsAsync(R.xml.remote_config_defaults)

No nosso caso, clique com o botão direito do mouse na pasta res e adicione a pasta xml se ela não existir, então adicione um arquivo XML com o nome de atributo welcome_image

Etapa 4: é a prática recomendada inicializar todas as coisas relacionadas ao Firebase na classe do aplicativo onCreate() conforme a seguir

package com.example.viewmodel

import android.app.Application
import android.util.Log
import com.google.firebase.FirebaseApp
import com.google.firebase.remoteconfig.FirebaseRemoteConfig
import com.google.firebase.remoteconfig.FirebaseRemoteConfigSettings

class SampleApplication : Application() {

    override fun onCreate() {
        super.onCreate()
        initFirebaseRemoteConfig()
    }

    private fun initFirebaseRemoteConfig() {
        FirebaseApp.initializeApp(this)
        FirebaseRemoteConfig.getInstance().apply {
            //defina isso durante o desenvolvimento
            val configSettings = FirebaseRemoteConfigSettings.Builder()
                .setMinimumFetchIntervalInSeconds(0)
                .build()
            setConfigSettingsAsync(configSettings)
            //defina isso durante o desenvolvimento

            setDefaultsAsync(R.xml.remote_config_defaults)
            fetchAndActivate().addOnCompleteListener { task ->
                val updated = task.result
                if (task.isSuccessful) {
                    val updated = task.result
                    Log.d("TAG", "Parâmetros de configuração atualizados: $updated")
                } else {
                    Log.d("TAG", "Parâmetros de configuração atualizados: $updated")
                }
            }
        }

    }
}

Isso inicializa o firebase e define os valores padrão usando setDefaultsAsync() e, em seguida, faz uma chamada de busca. Depois que os valores são recebidos do Firebase conforme usamos fetchAndActivate(), o que significa buscar e ativar os valores uma vez recebidos, o que significa que se alguém daquele ponto solicitar os parâmetros de remoteConfigObject, os valores mais recentes serão recebidos do servidor.

  1. Para buscar valores de parâmetro no back-end do Configuração remota, chame o método fetch(). Todos os valores definidos no back-end são buscados e armazenados no objeto do Configuração remota.
  2. Para disponibilizar valores de parâmetros buscados para seu aplicativo, chame o método activate().
  3. Para os casos em que deseja buscar e ativar valores em uma chamada, você pode usar uma solicitação fetchAndActivate() para buscar valores no back-end do Configuração remota e disponibilizá-los para o aplicativo

É assim que recebemos os valores mais recentes do FirebaseConsole.

Etapa 5: como recebemos os valores mais recentes do console, a próxima etapa é verificar a atualização obrigatória primeiro, pois ela tem alta prioridade. Para isso, precisamos obter a versão do aplicativo no dispositivo usando o método getAppVersion() e obter o min_version_of_app obtido do console e compará-los usando checkMandateVersionApplicable().

Como este aplicativo possui apenas separador de pontos para o nome da versão, usamos apenas o delimitador “.” ponto usando o método getAppVersionWithoutAlphaNumeric(). Caso contrário, verificamos que a versão do aplicativo e latest_version_of_app são os mesmos; caso contrário, mostraremos um pop-up de atualização opcional; no entanto, se esta condição também for passada, o usuário pode mover para o fluxo normal. É melhor fazer isso no SplashActivity, pois seria a tela inicial de inicialização.

package com.example.viewmodel

import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
import android.net.Uri
import android.os.Bundle
import android.text.TextUtils
import android.util.Log
import android.widget.Toast
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity
import androidx.lifecycle.ViewModelProvider
import com.google.firebase.remoteconfig.FirebaseRemoteConfig

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_sample)
        
        checkForUpdate()
    }

    private fun checkForUpdate() {

        val appVersion: String = getAppVersion(this)
        val remoteConfig = FirebaseRemoteConfig.getInstance()

        val currentVersion =
            remoteConfig.getString("min_version_of_app")
        val minVersion =
            remoteConfig.getString("latest_version_of_app")
        if (!TextUtils.isEmpty(minVersion) && !TextUtils.isEmpty(appVersion) && checkMandateVersionApplicable(
                getAppVersionWithoutAlphaNumeric(minVersion),
                getAppVersionWithoutAlphaNumeric(appVersion)
            )
        ) {
            onUpdateNeeded(true)
        } else if (!TextUtils.isEmpty(currentVersion) && !TextUtils.isEmpty(appVersion) && !TextUtils.equals(
                currentVersion,
                appVersion
            )
        ) {
            onUpdateNeeded(false)
        } else {
            moveForward()
        }
    }

    private fun checkMandateVersionApplicable(
        minVersion: String,
        appVersion: String
    ): Boolean {
        return try {
            val minVersionInt = minVersion.toInt()
            val appVersionInt = appVersion.toInt()
            minVersionInt > appVersionInt
        } catch (exp: NumberFormatException) {
            false
        }
    }

    private fun getAppVersion(context: Context): String {
        var result: String? = ""
        try {
            result = context.packageManager
                .getPackageInfo(context.packageName, 0).versionName
        } catch (e: PackageManager.NameNotFoundException) {
            Log.e("TAG", e.message)
        }
        return result?:""
    }

    private fun getAppVersionWithoutAlphaNumeric(result: String): String {
        var version_str = ""
        version_str = result.replace(".", "")
        return version_str
    }

    private fun onUpdateNeeded(isMandatoryUpdate: Boolean) {
        val dialogBuilder = AlertDialog.Builder(this)
            .setTitle(getString(R.string.update_app))
            .setCancelable(false)
            .setMessage(if (isMandatoryUpdate) getString(R.string.dialog_update_available_message) else "Uma nova versão foi encontrada na Play Store, atualize para melhor uso.")
            .setPositiveButton(getString(R.string.update_now))
            { dialog, which ->
                openAppOnPlayStore(this, null)
            }

        if (!isMandatoryUpdate) {
            dialogBuilder.setNegativeButton(getString(R.string.later)) { dialog, which ->
                moveForward()
                dialog?.dismiss()
            }.create()
        }
        val dialog: AlertDialog = dialogBuilder.create()
        dialog.show()
    }

    private fun moveForward() {
        Toast.makeText(this, "Intenção da próxima página", Toast.LENGTH_SHORT).show()
    }

    fun openAppOnPlayStore(ctx: Context, package_name: String?) {
        var package_name = package_name
        if (package_name == null) {
            package_name = ctx.packageName
        }
        val uri = Uri.parse("market://details?id=$package_name")
        openURI(ctx, uri, "Play Store não encontrada em seu dispositivo")
    }

    fun openURI(
        ctx: Context,
        uri: Uri?,
        error_msg: String?
    ) {
        val i = Intent(Intent.ACTION_VIEW, uri)
        i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
        i.addFlags(Intent.FLAG_ACTIVITY_NO_HISTORY)
        if (ctx.packageManager.queryIntentActivities(i, 0).size > 0) {
            ctx.startActivity(i)
        } else if (error_msg != null) {
            Toast.makeText(this, error_msg, Toast.LENGTH_SHORT).show()
        }
    }

}

 

Resultado:

 

Por favor, deixe-me saber suas sugestões e comentários.