From d76f1a0d930c8e187b5a7c3b6ffc66c48a469875 Mon Sep 17 00:00:00 2001 From: pedrofsn Date: Sat, 31 Oct 2020 16:23:07 -0300 Subject: [PATCH 1/3] Adding remote config constants to check and force app updates --- app/src/main/java/ro/code4/monitorizarevot/helper/Constants.kt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/src/main/java/ro/code4/monitorizarevot/helper/Constants.kt b/app/src/main/java/ro/code4/monitorizarevot/helper/Constants.kt index cdfc412c..773f47b7 100644 --- a/app/src/main/java/ro/code4/monitorizarevot/helper/Constants.kt +++ b/app/src/main/java/ro/code4/monitorizarevot/helper/Constants.kt @@ -22,6 +22,8 @@ object Constants { const val REMOTE_CONFIG_PRIVACY_POLICY_URL = "privacy_policy_url" const val REMOTE_CONFIG_OBSERVER_GUIDE_URL = "observer_guide_url" const val REMOTE_CONFIG_SAFETY_GUIDE_URL = "safety_guide_url" + const val REMOTE_CONFIG_UPDATE_CHECK = "android_check_update_available" + const val REMOTE_CONFIG_UPDATE_FORCE = "android_force_update" const val PUSH_NOTIFICATION_TITLE = "title" const val PUSH_NOTIFICATION_BODY = "body" From 52fe3b7d740b686c26c9c7c226f814a5852deedd Mon Sep 17 00:00:00 2001 From: pedrofsn Date: Sat, 31 Oct 2020 16:31:43 -0300 Subject: [PATCH 2/3] Preparing LoginViewModel to read update from remote config --- .../monitorizarevot/data/model/UpdateApp.kt | 15 +++++++++++++++ .../ui/login/LoginViewModel.kt | 19 +++++++++++++++++++ 2 files changed, 34 insertions(+) create mode 100644 app/src/main/java/ro/code4/monitorizarevot/data/model/UpdateApp.kt diff --git a/app/src/main/java/ro/code4/monitorizarevot/data/model/UpdateApp.kt b/app/src/main/java/ro/code4/monitorizarevot/data/model/UpdateApp.kt new file mode 100644 index 00000000..fbd6f912 --- /dev/null +++ b/app/src/main/java/ro/code4/monitorizarevot/data/model/UpdateApp.kt @@ -0,0 +1,15 @@ +package ro.code4.monitorizarevot.data.model + +import android.os.Parcelable +import kotlinx.android.parcel.Parcelize +import org.parceler.Parcel + +/* + CREATED BY @PEDROFSN IN 31/10/20 16:29 +*/ + +@Parcelize +data class UpdateApp( + val needUpdate : Boolean, + val forceUpdate : Boolean +) : Parcelable \ No newline at end of file diff --git a/app/src/main/java/ro/code4/monitorizarevot/ui/login/LoginViewModel.kt b/app/src/main/java/ro/code4/monitorizarevot/ui/login/LoginViewModel.kt index c83c1bd3..189ec3bb 100644 --- a/app/src/main/java/ro/code4/monitorizarevot/ui/login/LoginViewModel.kt +++ b/app/src/main/java/ro/code4/monitorizarevot/ui/login/LoginViewModel.kt @@ -2,22 +2,30 @@ package ro.code4.monitorizarevot.ui.login import android.content.SharedPreferences import android.util.Log +import androidx.lifecycle.Lifecycle import androidx.lifecycle.LiveData +import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.OnLifecycleEvent import com.google.firebase.analytics.FirebaseAnalytics import com.google.firebase.iid.FirebaseInstanceId +import com.google.firebase.remoteconfig.FirebaseRemoteConfig import io.reactivex.android.schedulers.AndroidSchedulers import io.reactivex.schedulers.Schedulers import org.koin.android.ext.android.inject import org.koin.core.inject import ro.code4.monitorizarevot.BuildConfig import ro.code4.monitorizarevot.analytics.Event +import ro.code4.monitorizarevot.data.model.UpdateApp import ro.code4.monitorizarevot.data.model.User import ro.code4.monitorizarevot.data.model.response.LoginResponse import ro.code4.monitorizarevot.helper.* +import ro.code4.monitorizarevot.helper.Constants.REMOTE_CONFIG_UPDATE_CHECK +import ro.code4.monitorizarevot.helper.Constants.REMOTE_CONFIG_UPDATE_FORCE import ro.code4.monitorizarevot.repositories.Repository import ro.code4.monitorizarevot.ui.base.BaseViewModel import ro.code4.monitorizarevot.ui.onboarding.OnboardingActivity import ro.code4.monitorizarevot.ui.section.PollingStationActivity +import java.util.* class LoginViewModel : BaseViewModel() { @@ -26,8 +34,19 @@ class LoginViewModel : BaseViewModel() { private val firebaseAnalytics: FirebaseAnalytics by inject() private val loginLiveData = SingleLiveEvent>>() + private val updates = SingleLiveEvent() + + @OnLifecycleEvent(Lifecycle.Event.ON_RESUME) + fun shouldUpdateAppVersion() { + FirebaseRemoteConfig.getInstance().apply { + val needForceUpdate = getBoolean(REMOTE_CONFIG_UPDATE_FORCE) + val needUpdate = getBoolean(REMOTE_CONFIG_UPDATE_CHECK) + updates.postValue(UpdateApp(needUpdate, needForceUpdate)) + } + } fun loggedIn(): LiveData>> = loginLiveData + fun needUpdate(): LiveData = updates fun login(phone: String, password: String) { loginLiveData.postValue(Result.Loading) From d384a591bd77d10b1933f137e5147824e7703c72 Mon Sep 17 00:00:00 2001 From: pedrofsn Date: Sat, 31 Oct 2020 16:54:36 -0300 Subject: [PATCH 3/3] Check app updates --- app/build.gradle | 1 + .../monitorizarevot/data/model/UpdateApp.kt | 4 +-- .../ro/code4/monitorizarevot/helper/Utils.kt | 10 +++++++ .../monitorizarevot/ui/login/LoginActivity.kt | 27 +++++++++++++++++++ .../ui/login/LoginViewModel.kt | 18 +++++-------- app/src/main/res/values-ro-rRO/strings.xml | 4 +++ app/src/main/res/values/strings.xml | 4 +++ 7 files changed, 54 insertions(+), 14 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 2190aafd..c2d44ab1 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -69,6 +69,7 @@ android { } debug { + debuggable true minifyEnabled false manifestPlaceholders = [enableCrashReporting: "false"] } diff --git a/app/src/main/java/ro/code4/monitorizarevot/data/model/UpdateApp.kt b/app/src/main/java/ro/code4/monitorizarevot/data/model/UpdateApp.kt index fbd6f912..d6f57e1f 100644 --- a/app/src/main/java/ro/code4/monitorizarevot/data/model/UpdateApp.kt +++ b/app/src/main/java/ro/code4/monitorizarevot/data/model/UpdateApp.kt @@ -10,6 +10,6 @@ import org.parceler.Parcel @Parcelize data class UpdateApp( - val needUpdate : Boolean, - val forceUpdate : Boolean + val hasUpdate : Boolean, + val needForceUpdate : Boolean ) : Parcelable \ No newline at end of file diff --git a/app/src/main/java/ro/code4/monitorizarevot/helper/Utils.kt b/app/src/main/java/ro/code4/monitorizarevot/helper/Utils.kt index 24895fae..367320fb 100644 --- a/app/src/main/java/ro/code4/monitorizarevot/helper/Utils.kt +++ b/app/src/main/java/ro/code4/monitorizarevot/helper/Utils.kt @@ -483,3 +483,13 @@ fun collapseKeyboardIfFocusOutsideEditText( ?.hideSoftInputFromWindow(newFocusedView.windowToken, 0) } } + +fun Context.openAppInPlayStore(appPackageName: String = packageName) { + val uri = try { + Uri.parse("market://details?id=$appPackageName") + } catch (exception: ActivityNotFoundException) { + Uri.parse("https://play.google.com/store/apps/details?id=$appPackageName") + } + val intent = Intent(Intent.ACTION_VIEW, uri) + startActivity(intent) +} diff --git a/app/src/main/java/ro/code4/monitorizarevot/ui/login/LoginActivity.kt b/app/src/main/java/ro/code4/monitorizarevot/ui/login/LoginActivity.kt index 650e56dc..54f600bb 100644 --- a/app/src/main/java/ro/code4/monitorizarevot/ui/login/LoginActivity.kt +++ b/app/src/main/java/ro/code4/monitorizarevot/ui/login/LoginActivity.kt @@ -1,15 +1,20 @@ package ro.code4.monitorizarevot.ui.login +import android.content.DialogInterface import android.os.Bundle import android.text.TextWatcher +import androidx.appcompat.app.AlertDialog import androidx.lifecycle.Observer +import androidx.lifecycle.observe import com.google.android.material.snackbar.Snackbar import kotlinx.android.synthetic.main.activity_login.* import org.koin.android.viewmodel.ext.android.viewModel import ro.code4.monitorizarevot.BuildConfig import ro.code4.monitorizarevot.R +import ro.code4.monitorizarevot.data.model.UpdateApp import ro.code4.monitorizarevot.helper.TextWatcherDelegate import ro.code4.monitorizarevot.helper.isOnline +import ro.code4.monitorizarevot.helper.openAppInPlayStore import ro.code4.monitorizarevot.helper.startActivityWithoutTrace import ro.code4.monitorizarevot.ui.base.BaseAnalyticsActivity import ro.code4.monitorizarevot.widget.ProgressDialogFragment @@ -51,6 +56,28 @@ class LoginActivity : BaseAnalyticsActivity() { loginButton.isEnabled = !p0.isNullOrEmpty() && !phone.text.isNullOrEmpty() } }) + observeAppUpdates() + } + + private fun observeAppUpdates() { + viewModel.shouldUpdateAppVersion().observe(this) { shouldUpdateApp(update = it) } + } + + private fun shouldUpdateApp(update: UpdateApp) { + if (update.hasUpdate) { + AlertDialog.Builder(this) + .setTitle(R.string.app_update_dialog_title) + .setMessage(R.string.app_update_dialog_message) + .setCancelable(update.needForceUpdate.not()) + .setPositiveButton(android.R.string.ok) { dialog: DialogInterface, _ -> + if (update.needForceUpdate) { + openAppInPlayStore() + } + dialog.dismiss() + } + .create() + .show() + } } override fun onDestroy() { diff --git a/app/src/main/java/ro/code4/monitorizarevot/ui/login/LoginViewModel.kt b/app/src/main/java/ro/code4/monitorizarevot/ui/login/LoginViewModel.kt index 189ec3bb..155954f8 100644 --- a/app/src/main/java/ro/code4/monitorizarevot/ui/login/LoginViewModel.kt +++ b/app/src/main/java/ro/code4/monitorizarevot/ui/login/LoginViewModel.kt @@ -1,17 +1,13 @@ package ro.code4.monitorizarevot.ui.login import android.content.SharedPreferences -import android.util.Log -import androidx.lifecycle.Lifecycle import androidx.lifecycle.LiveData -import androidx.lifecycle.MutableLiveData -import androidx.lifecycle.OnLifecycleEvent +import com.google.firebase.FirebaseApp import com.google.firebase.analytics.FirebaseAnalytics import com.google.firebase.iid.FirebaseInstanceId import com.google.firebase.remoteconfig.FirebaseRemoteConfig import io.reactivex.android.schedulers.AndroidSchedulers import io.reactivex.schedulers.Schedulers -import org.koin.android.ext.android.inject import org.koin.core.inject import ro.code4.monitorizarevot.BuildConfig import ro.code4.monitorizarevot.analytics.Event @@ -25,7 +21,6 @@ import ro.code4.monitorizarevot.repositories.Repository import ro.code4.monitorizarevot.ui.base.BaseViewModel import ro.code4.monitorizarevot.ui.onboarding.OnboardingActivity import ro.code4.monitorizarevot.ui.section.PollingStationActivity -import java.util.* class LoginViewModel : BaseViewModel() { @@ -36,17 +31,16 @@ class LoginViewModel : BaseViewModel() { private val loginLiveData = SingleLiveEvent>>() private val updates = SingleLiveEvent() - @OnLifecycleEvent(Lifecycle.Event.ON_RESUME) - fun shouldUpdateAppVersion() { - FirebaseRemoteConfig.getInstance().apply { + fun shouldUpdateAppVersion(): LiveData { + FirebaseRemoteConfig.getInstance().run { val needForceUpdate = getBoolean(REMOTE_CONFIG_UPDATE_FORCE) - val needUpdate = getBoolean(REMOTE_CONFIG_UPDATE_CHECK) - updates.postValue(UpdateApp(needUpdate, needForceUpdate)) + val hasUpdate = getBoolean(REMOTE_CONFIG_UPDATE_CHECK) + updates.postValue(UpdateApp(hasUpdate, needForceUpdate)) } + return updates } fun loggedIn(): LiveData>> = loginLiveData - fun needUpdate(): LiveData = updates fun login(phone: String, password: String) { loginLiveData.postValue(Result.Loading) diff --git a/app/src/main/res/values-ro-rRO/strings.xml b/app/src/main/res/values-ro-rRO/strings.xml index 8a2f755f..1fa67b73 100644 --- a/app/src/main/res/values-ro-rRO/strings.xml +++ b/app/src/main/res/values-ro-rRO/strings.xml @@ -150,4 +150,8 @@ Politică de confidențialitate Trimiteți email prin v%1$S build %2$d aplicație dezvoltată de + + + Actualizare disponibilă + O nouă versiune a aplicației este disponibilă. Te rugăm să faci update pentru a folosi cele mai recente funcționalități. \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 3b39e68b..6a48e67d 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -155,4 +155,8 @@ Privacy policy Send email via v%1$S build %2$d app developed by + + + App UpdateAvailable + A new app version is available. Please update the app to use the latest features.