Check For Update In App And Display Dialog In Android/Kotlin

HOW TO CHECK FOR UPDATE IN APP AND DISPLAY DIALOG IN ANDROID/Kotlin USING FIREBASE REMOTE CONFIG


Please Subscribe Youtube| Like Facebook | Follow Twitter

For Java follow this article

Introduction

In this article we will learn how to implement update check and then display alert dialog in your android app using firebase remote configuration. When you have updated your app in Play Store and want to display alert dialog to notify users that are using previous/older version of your app to go to Play Store for update.

Our Example

In our example code we will first setup firebase remote config and save our current app version code value to server-side configuration parameter (key/value) at firebase. On app start we will check current value of our app version code and then compare it to the remote value at firebase. If remote app version code value is greater than our current app version code value then we will display alert dialog to user for updating app. Therefore whenever we update our app at Play Store, we will save that current app version code value in firebase remote config.

Requirements:

  1. Android Project/App in which update check to be added
  2. Firebase Project

Note: First you need to create Firebase Project and then connect your app to it. You can follow this article to set it up.

Tip: You can change app version code at app level build.gradle file in defaultConfig block.

Steps

Follow below steps

1) Create remote key/value parameter version_code at firebase remote config.

2) Implement update check logic

3) Run and test your app.

1) Create remote key/value parameter version_code at firebase remote config.

First go to Firebase and open your Firebase Project and click on Remote Config.

Enter details in parameter object: Parameter Key = version_code, Default value = 0, Description = Check For Update. Then click on add parameter.

After adding parameter click on publish changes

2) Implement update check logic

First add firebase config library at app level build.gradle file

 implementation 'com.google.firebase:firebase-config:19.1.4'

On app start we have called initRemoteConfig() method, which will initialize Firebase Remote Config Object. We have set current app version code value of our running app in default Hash Map and  added it in setDefaultsAsync() method. Default values are used for safety if on backend version_code is not set in remote config.

Then we have set configuration of remote config using setConfigSettingsAsync() method in which minimum fetch interval value is specified. This defines minimum interval between successive fetches calls in seconds. Default value is 12 (12 hours) but during development set it to 0 to retrieve value from firebase server immediately. During production set it to 12 to save battery and data consumption.

After setting we have called fetch() method which will fetch value from remote config, on complete we have called activate() method which will activate most recently fetch config value from firebase server. After activation we will get latest app version code value from remote config and will call checkForUpdate() method to check if update is available.

private fun initRemoteConfig() {

	mFirebaseRemoteConfig = FirebaseRemoteConfig.getInstance()

	//Setting the Default Map Value with the current app version code
	//default values are used for safety if on backend version_code is not set in remote config
	val firebaseDefaultMap = HashMap<String, Any>()
	firebaseDefaultMap[VERSION_CODE_KEY] = currentVersionCode
	mFirebaseRemoteConfig!!.setDefaultsAsync(firebaseDefaultMap)

	//setMinimumFetchIntervalInSeconds to 0 during development to fast retrieve the values
	//in production set it to 12 which means checks for firebase remote config values for every 12 hours
	mFirebaseRemoteConfig!!.setConfigSettingsAsync(
			FirebaseRemoteConfigSettings.Builder()
					.setMinimumFetchIntervalInSeconds(TimeUnit.HOURS.toSeconds(0))
					.build())

	//Fetching remote firebase version_code value here
	mFirebaseRemoteConfig!!.fetch().addOnCompleteListener { task ->
		if (task.isSuccessful) {
			//activate most recently fetch config value
			mFirebaseRemoteConfig!!.activate().addOnCompleteListener { task ->
				if (task.isSuccessful) {
					//calling function to check if new version is available or not
					val latestAppVersion = mFirebaseRemoteConfig!!.getDouble(VERSION_CODE_KEY).toInt()
					runOnUiThread { checkForUpdate(latestAppVersion) }
				}
			}
		}
	}
}

checkForUpdate() method will compare and display alert dialog to user for updating app If remote app version code value is greater than our current running app version code value.

private fun checkForUpdate(latestAppVersion: Int) {

	if (latestAppVersion > currentVersionCode) {
		val builder = AlertDialog.Builder(this)
		builder.setTitle("Update")
		builder.setMessage("New Version Available. Please Update App")
		builder.setPositiveButton("Ok") { dialog, which ->
			goToPlayStore()
			updateDailog!!.dismiss()
		}
		builder.setNegativeButton("Cancel") { dialog, which -> updateDailog!!.dismiss() }
		// create and show the alert dialog
		updateDailog = builder.create()
		updateDailog!!.show()
	}
}

getCurrentVersionCode() method gets version code of current running app on user device.

private val currentVersionCode: Int
	get() {
		var versionCode = 1
		try {
			val pInfo = applicationContext.packageManager.getPackageInfo(applicationContext.packageName, 0)
			versionCode = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
				pInfo.longVersionCode.toInt()
			} else {
				pInfo.versionCode
			}
		} catch (e: PackageManager.NameNotFoundException) {
			//log exception
		}
		return versionCode
	}

goToPlayStore() method navigates user to playstore

private fun goToPlayStore() {
	try {
		val appStoreIntent = Intent(Intent.ACTION_VIEW, Uri.parse("market://details?id=$packageName"))
		appStoreIntent.setPackage("com.android.vending")
		startActivity(appStoreIntent)
	} catch (exception: ActivityNotFoundException) {
		startActivity(Intent(Intent.ACTION_VIEW, Uri.parse("https://play.google.com/store/apps/details?id=$packageName")))
	}
}

Whole Code

project level build.gradle

buildscript {
    ext.kotlin_version = '1.4.0-rc'
    repositories {
        jcenter()
        google()
        maven { url 'https://dl.bintray.com/kotlin/kotlin-eap' }
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:3.5.3'
        classpath 'com.google.gms:google-services:4.3.3'
        classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"

    }
}

allprojects {
    repositories {
        google()
        jcenter()
        
    }
}

task clean(type: Delete) {
    delete rootProject.buildDir
}

app level build.gradle

apply plugin: 'com.android.application'
apply plugin: 'kotlin-android-extensions'
apply plugin: 'kotlin-android'
apply plugin: 'com.google.gms.google-services'
android {
    compileSdkVersion 28
    buildToolsVersion "29.0.3"
    defaultConfig {
        applicationId "com.programtown.example"
        minSdkVersion 17
        targetSdkVersion 28
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }
}

dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation 'androidx.appcompat:appcompat:1.0.2'
    implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'androidx.test.ext:junit:1.1.0'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1'

    implementation 'com.google.firebase:firebase-config:19.1.4'
    implementation "androidx.core:core-ktx:+"
    implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"

}
repositories {
    maven { url 'https://dl.bintray.com/kotlin/kotlin-eap' }
    mavenCentral()
}

Here you can change version code (versionCode) of your app on new updates.

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">


</androidx.constraintlayout.widget.ConstraintLayout>

MainActivity.kt

package com.programtown.example

import android.content.ActivityNotFoundException
import android.content.Intent
import android.content.pm.PackageManager
import android.net.Uri
import android.os.Build
import android.os.Bundle
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity
import com.google.firebase.remoteconfig.FirebaseRemoteConfig
import com.google.firebase.remoteconfig.FirebaseRemoteConfigSettings
import java.util.*
import java.util.concurrent.TimeUnit

class MainActivity : AppCompatActivity() {

    private var mFirebaseRemoteConfig: FirebaseRemoteConfig? = null
    private var updateDailog: AlertDialog? = null

    override fun onCreate(savedInstanceState: Bundle?) {

        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        initRemoteConfig()
    }

    private fun initRemoteConfig() {

        mFirebaseRemoteConfig = FirebaseRemoteConfig.getInstance()

        //Setting the Default Map Value with the current app version code
        //default values are used for safety if on backend version_code is not set in remote config
        val firebaseDefaultMap = HashMap<String, Any>()
        firebaseDefaultMap[VERSION_CODE_KEY] = currentVersionCode
        mFirebaseRemoteConfig!!.setDefaultsAsync(firebaseDefaultMap)

        //setMinimumFetchIntervalInSeconds to 0 during development to fast retrieve the values
        //in production set it to 12 which means checks for firebase remote config values for every 12 hours
        mFirebaseRemoteConfig!!.setConfigSettingsAsync(
                FirebaseRemoteConfigSettings.Builder()
                        .setMinimumFetchIntervalInSeconds(TimeUnit.HOURS.toSeconds(0))
                        .build())

        //Fetching remote firebase version_code value here
        mFirebaseRemoteConfig!!.fetch().addOnCompleteListener { task ->
            if (task.isSuccessful) {
                //activate most recently fetch config value
                mFirebaseRemoteConfig!!.activate().addOnCompleteListener { task ->
                    if (task.isSuccessful) {
                        //calling function to check if new version is available or not
                        val latestAppVersion = mFirebaseRemoteConfig!!.getDouble(VERSION_CODE_KEY).toInt()
                        runOnUiThread { checkForUpdate(latestAppVersion) }
                    }
                }
            }
        }
    }

    private fun checkForUpdate(latestAppVersion: Int) {

        if (latestAppVersion > currentVersionCode) {
            val builder = AlertDialog.Builder(this)
            builder.setTitle("Update")
            builder.setMessage("New Version Available. Please Update App")
            builder.setPositiveButton("Ok") { dialog, which ->
                goToPlayStore()
                updateDailog!!.dismiss()
            }
            builder.setNegativeButton("Cancel") { dialog, which -> updateDailog!!.dismiss() }
            // create and show the alert dialog
            updateDailog = builder.create()
            updateDailog!!.show()
        }
    }

    private val currentVersionCode: Int
        get() {
            var versionCode = 1
            try {
                val pInfo = applicationContext.packageManager.getPackageInfo(applicationContext.packageName, 0)
                versionCode = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
                    pInfo.longVersionCode.toInt()
                } else {
                    pInfo.versionCode
                }
            } catch (e: PackageManager.NameNotFoundException) {
                //log exception
            }
            return versionCode
        }

    private fun goToPlayStore() {
        try {
            val appStoreIntent = Intent(Intent.ACTION_VIEW, Uri.parse("market://details?id=$packageName"))
            appStoreIntent.setPackage("com.android.vending")
            startActivity(appStoreIntent)
        } catch (exception: ActivityNotFoundException) {
            startActivity(Intent(Intent.ACTION_VIEW, Uri.parse("https://play.google.com/store/apps/details?id=$packageName")))
        }
    }

    companion object {
        private const val VERSION_CODE_KEY = "version_code"
    }
}

3) Run and test your app

Initially Our App version code is 1. We have updated our app and its version code is now became 2.

    defaultConfig {
        applicationId "com.programtown.example"
        minSdkVersion 17
        targetSdkVersion 28
        versionCode 2
        versionName "1.0"
        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
    }

We have updated our app on play store, now we will change version code at firebase remote config to display update dialog to users who are using older version (version code 1) of app.

Go to remote config change version_code value to 2 and click on update then publish changes.

After updating value at firebase config, old version app user will get update dialog notification.

Conclusion

So in this post we have learned how to implement update check and display alert dialog in android application.

Please Subscribe Youtube| Like Facebook | Follow Twitter


Leave a Reply

Your email address will not be published. Required fields are marked *