From 0451a0bf9a44ab80b0bb15825eb7e865a8c05dcf Mon Sep 17 00:00:00 2001 From: Jtoliveira Date: Fri, 26 Mar 2021 13:27:39 +0000 Subject: [PATCH 1/9] Add messy first version of solution to tackle issue #90, WEB_API_HOST now stored in SharedPreferences, ErrorActivity.kt refreshes the stored value in case of non reachable API and opens browser if all fails --- Project/.idea/compiler.xml | 6 +++ Project/.idea/gradle.xml | 1 + Project/.idea/misc.xml | 2 +- .../android/common/IonApplication.kt | 14 +++++- .../ionproject/android/error/ErrorActivity.kt | 42 ++++++++++++++++ .../android/error/ErrorViewModel.kt | 50 +++++++++++++++++++ .../android/error/ErrorViewModelProvider.kt | 18 +++++++ .../ionproject/android/error/RemoteConfig.kt | 8 +++ .../src/main/res/layout/activity_error.xml | 9 ++++ .../app/src/main/res/values-pt/strings.xml | 1 + Project/app/src/main/res/values/strings.xml | 1 + 11 files changed, 149 insertions(+), 3 deletions(-) create mode 100644 Project/.idea/compiler.xml create mode 100644 Project/app/src/main/java/org/ionproject/android/error/ErrorViewModel.kt create mode 100644 Project/app/src/main/java/org/ionproject/android/error/ErrorViewModelProvider.kt create mode 100644 Project/app/src/main/java/org/ionproject/android/error/RemoteConfig.kt diff --git a/Project/.idea/compiler.xml b/Project/.idea/compiler.xml new file mode 100644 index 00000000..61a9130c --- /dev/null +++ b/Project/.idea/compiler.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/Project/.idea/gradle.xml b/Project/.idea/gradle.xml index 5cd135a0..9bba60da 100644 --- a/Project/.idea/gradle.xml +++ b/Project/.idea/gradle.xml @@ -14,6 +14,7 @@ diff --git a/Project/.idea/misc.xml b/Project/.idea/misc.xml index f5c6d9eb..58918f50 100644 --- a/Project/.idea/misc.xml +++ b/Project/.idea/misc.xml @@ -1,4 +1,4 @@ - + \ No newline at end of file diff --git a/Project/app/src/main/java/org/ionproject/android/common/IonApplication.kt b/Project/app/src/main/java/org/ionproject/android/common/IonApplication.kt index 4dbbfe52..559c1c0b 100644 --- a/Project/app/src/main/java/org/ionproject/android/common/IonApplication.kt +++ b/Project/app/src/main/java/org/ionproject/android/common/IonApplication.kt @@ -1,6 +1,8 @@ package org.ionproject.android.common import android.app.Application +import android.content.Context +import android.content.SharedPreferences import androidx.room.Room import org.ionproject.android.common.connectivity.ConnectivityObservableFactory import org.ionproject.android.common.connectivity.IConnectivityObservable @@ -37,6 +39,13 @@ class IonApplication : Application() { lateinit var globalExceptionHandler: GlobalExceptionHandler private set lateinit var preferences: Preferences private set lateinit var connectivityObservable: IConnectivityObservable private set + + + lateinit var sharedPreferences: SharedPreferences + + fun saveAPIURLToSharedPreferences(newURL:String) = + sharedPreferences.edit().putString("WEB_API_HOST", newURL).apply() + } override fun onCreate() { @@ -56,13 +65,15 @@ class IonApplication : Application() { val ionMapper = JacksonIonMapper() + sharedPreferences = getSharedPreferences("API_INFO_PREFERENCES", Context.MODE_PRIVATE) + //------- Using mocked API ----------- //val webAPI = MockIonWebAPI(ionMapper) //------------------------------------ //------- Using real API ------------- val retrofit = Retrofit.Builder() - .baseUrl(WEB_API_HOST) + .baseUrl(sharedPreferences.getString("WEB_API_HOST", WEB_API_HOST)) .addConverterFactory(ScalarsConverterFactory.create()) .build() @@ -107,5 +118,4 @@ class IonApplication : Application() { Preferences(applicationContext) connectivityObservable = ConnectivityObservableFactory.create(applicationContext) } - } diff --git a/Project/app/src/main/java/org/ionproject/android/error/ErrorActivity.kt b/Project/app/src/main/java/org/ionproject/android/error/ErrorActivity.kt index 7710c971..fce9a511 100644 --- a/Project/app/src/main/java/org/ionproject/android/error/ErrorActivity.kt +++ b/Project/app/src/main/java/org/ionproject/android/error/ErrorActivity.kt @@ -1,16 +1,31 @@ package org.ionproject.android.error +import android.content.Context +import android.content.Intent +import android.net.Uri import android.os.Bundle +import android.util.Log +import android.widget.Toast import androidx.appcompat.app.AppCompatActivity +import androidx.lifecycle.ViewModelProvider import kotlinx.android.synthetic.main.activity_error.* import org.ionproject.android.R +import org.ionproject.android.common.IonApplication import org.ionproject.android.common.addGradientBackground +import org.ionproject.android.common.ionwebapi.WEB_API_HOST +import org.ionproject.android.loading.LoadingActivity +import org.ionproject.android.loading.LoadingViewModel +import org.ionproject.android.loading.LoadingViewModelProvider // Random value key used to pass the error message to [ErrorActivity] via the intent const val ERROR_KEY = "12xp3m91x0meh1" class ErrorActivity : AppCompatActivity() { + private val errorViewModel by lazy(LazyThreadSafetyMode.NONE) { + ViewModelProvider(this, ErrorViewModelProvider())[ErrorViewModel::class.java] + } + override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_error) @@ -21,10 +36,37 @@ class ErrorActivity : AppCompatActivity() { finish() } + //get the remote config data + button_activity_error_tryAgain.setOnClickListener{ + errorViewModel.makeRequest() + } + // Apply error message if it was passed via the intent intent.getStringExtra(ERROR_KEY)?.apply { textview_error_activity_message.text = this } + errorViewModel.observeErrorLiveData(this) { + + Log.d("pls", it) + + if (it == null) { + Toast.makeText(this, "Check phone connection", Toast.LENGTH_LONG).show() + } else { + if (it == WEB_API_HOST) //the data is fresh which means the API is down + openingTheBrowser() + else{ //the data was not fresh, we update the value stored in the Shared Preferences and try again + IonApplication.saveAPIURLToSharedPreferences(it) + startActivity(Intent(this, LoadingActivity::class.java)) + } + } + } + } + + //if the remote config link is the same as the link in the app, + //it means the API is down and for now we redirect to the ISEL website + private fun openingTheBrowser(){ + val iselURL = "https://www.isel.pt" + startActivity(Intent(Intent.ACTION_VIEW, Uri.parse(iselURL))) } } diff --git a/Project/app/src/main/java/org/ionproject/android/error/ErrorViewModel.kt b/Project/app/src/main/java/org/ionproject/android/error/ErrorViewModel.kt new file mode 100644 index 00000000..25271824 --- /dev/null +++ b/Project/app/src/main/java/org/ionproject/android/error/ErrorViewModel.kt @@ -0,0 +1,50 @@ +package org.ionproject.android.error + +import androidx.lifecycle.LifecycleOwner +import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.Observer +import androidx.lifecycle.ViewModel +import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper +import com.fasterxml.jackson.module.kotlin.readValue +import okhttp3.OkHttpClient +import okhttp3.Request +import okhttp3.Response +import java.util.concurrent.Executor +import java.util.concurrent.Executors + +class ErrorViewModel: ViewModel() { + + // jus trying to make this work, will be updated in the future + + companion object { + private val worker: Executor = Executors.newSingleThreadExecutor() + } + + private val remoteConfigURL = "https://raw.githubusercontent.com/Jtoliveira/test/main/Remote_Config.json" + + private val remoteConfigLiveData = MutableLiveData() + + fun makeRequest(){ + + val okHttpClient = OkHttpClient() + + worker.execute{ + remoteConfigLiveData.postValue(parseResponse(okHttpClient.newCall(createRequest(remoteConfigURL)).execute())) + } + } + + private fun createRequest(url: String) : Request = + Request.Builder().url(url).build() + + private fun parseResponse(response: Response): String { + + return jacksonObjectMapper() + .readValue(response.body()?.string()!!) + .api_link + + } + + fun observeErrorLiveData(lifecycleOwner: LifecycleOwner, onUpdate: (String?) -> Unit) { + remoteConfigLiveData.observe(lifecycleOwner, Observer { onUpdate(it) }) + } +} \ No newline at end of file diff --git a/Project/app/src/main/java/org/ionproject/android/error/ErrorViewModelProvider.kt b/Project/app/src/main/java/org/ionproject/android/error/ErrorViewModelProvider.kt new file mode 100644 index 00000000..1bfe547d --- /dev/null +++ b/Project/app/src/main/java/org/ionproject/android/error/ErrorViewModelProvider.kt @@ -0,0 +1,18 @@ +package org.ionproject.android.error + +import androidx.lifecycle.ViewModel +import androidx.lifecycle.ViewModelProvider +import org.ionproject.android.common.IonApplication +import org.ionproject.android.loading.LoadingViewModel + +class ErrorViewModelProvider : ViewModelProvider.Factory { + + @Suppress("UNCHECKED_CAST") + override fun create(modelClass: Class): T { + return when (modelClass) { + ErrorViewModel::class.java -> ErrorViewModel() + else -> throw IllegalArgumentException("Class $modelClass is not valid for this provider") + } as T + } + +} \ No newline at end of file diff --git a/Project/app/src/main/java/org/ionproject/android/error/RemoteConfig.kt b/Project/app/src/main/java/org/ionproject/android/error/RemoteConfig.kt new file mode 100644 index 00000000..16e9e66e --- /dev/null +++ b/Project/app/src/main/java/org/ionproject/android/error/RemoteConfig.kt @@ -0,0 +1,8 @@ +package org.ionproject.android.error + +import com.fasterxml.jackson.annotation.JsonProperty + +data class RemoteConfig( + @JsonProperty("api_link") + var api_link: String + ) diff --git a/Project/app/src/main/res/layout/activity_error.xml b/Project/app/src/main/res/layout/activity_error.xml index 8fd80400..7e803a41 100644 --- a/Project/app/src/main/res/layout/activity_error.xml +++ b/Project/app/src/main/res/layout/activity_error.xml @@ -57,6 +57,15 @@ android:backgroundTint="@color/secondaryLightColor" android:text="@string/label_button_close_error" /> +