Confused about LiveData Observe - android-livedata

My situation is When user enter loading fragment, check LoggedIn, true go straight to MainFragment, false jump to LoginFramgnet.
here is LoadingFragment:
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
Logger.t(LoadingFragment::class.java.simpleName).i("onCreateView")
val binding = LoadingFragmentBinding.inflate(inflater, container, false)
subscribeUi()
return binding.root
}
fun subscribeUi(){
val factory: LoadingViewModelFactory = InjectorUtils.provideLoadingViewModelFactory()
val viewModel: LoadingViewModel = ViewModelProviders.of(this, factory).get(LoadingViewModel::class.java)
Logger.t(LoadingFragment::class.java.simpleName).i("viewModel = " + viewModel.toString())
viewModel.checkLogin()
viewModel.isToLogin.observe(viewLifecycleOwner, Observer {
if (it){
findNavController().navigate(R.id.action_loading_fragment_to_garden_fragment)
}else{
Logger.t(LoadingFragment::class.java.simpleName).i("to start login")
findNavController().navigate(R.id.start_login)
}
})
}
here is LoadingViewModel:
class LoadingViewModel(
private val loadingRepository: LoadingRepository
) : ViewModel() {
val isToLogin: MediatorLiveData<Boolean> = MediatorLiveData()
fun checkLogin(){
isToLogin.addSource(loadingRepository.checkLogin()) {
isToLogin.value = it
}
}
}
here is the Loadingrepository:
fun checkLogin() : MutableLiveData<Boolean> {
val data: MutableLiveData<Boolean> = MutableLiveData()
api.httpGet(SDBUrl.CHECK_LOGIN).enqueue(object : Callback<Map<String, Any>>{
override fun onFailure(call: Call<Map<String, Any>>, t: Throwable) {
data.value = false
}
override fun onResponse(call: Call<Map<String, Any>>, response: Response<Map<String, Any>>) {
val result = response.body()
if (result != null && result.containsKey("success")){
val isLogin = result["success"] as Boolean
data.value = isLogin
}else{
data.value = false
}
}
})
return data
}
when logged in, popbackto LoadingFragment,isToLogin observe execute else immediately, LoginFragment start agagin. when I debug, wait a while on LoginFragment popBackStack, then goback to Loading Fragment,isToLogin observe execute true.so I am very confused, how can I fix this.

Finally, I solved this problem as follow.
class LoadingViewModel(
private val loadingRepository: LoadingRepository
) : ViewModel() {
fun checkLogin(): MediatorLiveData<Boolean> {
val isToLogin : MediatorLiveData<Boolean> = MediatorLiveData()
isToLogin.addSource(loadingRepository.checkLogin()) {
Logger.t(LoadingViewModel::class.java.simpleName).i("it = $it")
isToLogin.value = it
}
return isToLogin
}
}
then in LoadingFragment,:
loadingViewModel.checkLogin().observe(viewLifecycleOwner, Observer {
Logger.i("isToLogin = $it")
if (it) {
findNavController().navigate(R.id.action_loading_fragment_to_garden_fragment)
} else {
findNavController().navigate(R.id.start_login)
}
})

Related

How to combine two foreach to one in Swift?

I have one function which is having some logic which have 2 foreach loop but i want to make code compact so I am trying to use compactmap
func getData() -> [String] {
var ids = [String]()
self.item?.connections?.forEach { connection in
connection.validLine?.forEach { line in
if let _ = line.connection?.links[LinkKey.dataGroups],
let dataGroups = line.dataGroupsCache, dataGroups.isContinue {
ids += checkinGroups.connections?.compactMap { $0.id } ?? []
}
}
}
return ids
}
so instead of 2 foreach i am trying to make in one by using self.item?.connections?.compactMap({ $0.validline }) but I am getting error saying "Type of expression is ambiguous without more context"
I don't see how you can do it without to forEach or compactMap. Here is a possible solution:
func getData() -> [String] {
return item?.connections?.compactMap { connection in
connection.validLine?.compactMap { line in
guard let _ = line.connection?.links[LinkKey.dataGroups], line.dataGroupsCache?.isContinue == true else { return nil }
return checkinGroups.connections?.compactMap(\.id)
}
}
}
Here's a translation of your post into something that is compilable and a direct translation into a version that doesn't use forEach.
I changed connectionIds to ids in your example because otherwise, you might as well just return [].
class Example {
func getData() -> [String] {
var ids = [String]()
self.item?.connections?.forEach { connection in
connection.validLine?.forEach { line in
if let _ = line.connection?.links[LinkKey.dataGroups],
let dataGroups = line.dataGroupsCache, dataGroups.isContinue {
ids += checkinGroups.connections?.compactMap { $0.id } ?? []
}
}
}
return ids
}
func getDataʹ() -> [String] {
guard let connections = item?.connections else { return [] }
let numberOfProperLines = connections.flatMap { $0.validLine ?? [] }
.filter { line in
if let _ = line.connection?.links[LinkKey.dataGroups],
let dataGroups = line.dataGroupsCache, dataGroups.isContinue {
return true
} else {
return false
}
}
.count
return (0..<numberOfProperLines).flatMap { _ in checkinGroups.connections?.compactMap(\.id) ?? [] }
}
var checkinGroups: CheckInGroups!
var item: Item!
}
enum LinkKey: Int {
case dataGroups
}
struct Item {
let connections: [Connection]?
}
struct Connection {
let id: String?
let validLine: [Line]?
let links: [LinkKey: Void]
}
struct Line {
let dataGroupsCache: DataGroups?
let connection: Connection?
}
struct DataGroups {
let isContinue: Bool
}
struct CheckInGroups {
let connections: [Connection]?
}

Android Compose keeps recomposing

I am writing a LazyColumn, items are LazyRows.
But the PasserItem keeps recomposing when gently swipe, I see log keep pringing and never stop.
I come here for help.
Why this happen, any state wrong?
I find tow ways stop it. remove items in MemoList2, or remove AnimatedVisibility in PasserItem2. Why these ways help?
Is my pattern implementing get-in animation with AnimatedVisibility proper?(I want anim run only once)
data class Passer(val username: String?, val memo: Array<String>)
MainActivity#onCreate
val live = MutableLiveData<Array<Passer>>()
live.value = Array(100) {
Passer("$it", arrayOf("$it", "$it"))
}
setContent {
PasserTheme {
Home2(live)
}
}
private const val TAG = "Home"
#Composable
fun Home2(passersLive: LiveData<Array<Passer>>) {
val passers = passersLive.observeAsState(initial = arrayOf())
Scaffold {
Box(
modifier = Modifier.fillMaxSize(),
content = { PasserList2(passers = passers.value) }
)
}
}
#OptIn(ExperimentalAnimationApi::class)
#Composable
private fun PasserList2(passers: Array<Passer>) {
Log.d(TAG, "PasserList")
LazyColumn(modifier = Modifier.fillMaxSize()) {
Log.d(TAG, "LazyColumn ${passers.size}")
items(passers.size) { index ->
PasserItem2(passer = passers[index])
if (index != passers.size - 1) {
Spacer(modifier = Modifier.height(32.dp))
}
}
}
}
#OptIn(ExperimentalAnimationApi::class)
#Composable
private fun PasserItem2(passer: Passer) {
Log.d(TAG, "PasserItem ${passer.username}")
var visibility by remember { mutableStateOf(false) }
LaunchedEffect(key1 = visibility) {
Log.d(TAG, "PasserItem LaunchedEffect")
visibility = true
}
Log.d(TAG, "PasserItem visible $visibility")
AnimatedVisibility(visible = visibility) {
Column(
modifier = Modifier
.fillMaxWidth()
.background(MaterialTheme.colors.onBackground.copy(alpha = 0.1F))
.padding(16.dp)
) {
MemoList2(passer.memo)
}
}
}
#Composable
private fun MemoList2(memo: Array<String>) {
Log.d(TAG, "MemoList")
LazyRow(modifier = Modifier.fillMaxWidth()) {
items(memo.size) { }
}
}
buildscript {
ext {
compose_version = '1.2.0-alpha07'
my_dependency = [:]
my_dependency.gradle = "com.android.tools.build:gradle:7.0.4"
my_dependency.kotlin_gradle = "org.jetbrains.kotlin:kotlin-gradle-plugin:1.6.10"
my_dependency.core_ktx = "androidx.core:core-ktx:1.7.0"
my_dependency.compose_ui_ui = "androidx.compose.ui:ui:1.2.0-alpha07"
my_dependency.compose_material = "androidx.compose.material:material:1.1.1"
my_dependency.compose_ui_tooling_preview = "androidx.compose.ui:ui-tooling-preview:1.1.1"
my_dependency.compose_activity = "androidx.activity:activity-compose:1.4.0"
my_dependency.compose_runtime_livedata = "androidx.compose.runtime:runtime-livedata:1.1.1"
my_dependency.compose_ui_tooling = "androidx.compose.ui:ui-tooling:1.1.1"
versions = [:]
versions.versionCode = 1
versions.versionName = "1.0"
versions.compileSdkVersion = 31
versions.buildToolsVersion = '33.0.1'
versions.minSdk = 30
versions.targetSdk = 31
}
repositories {
google()
mavenCentral()
}
dependencies {
classpath "$my_dependency.gradle"
classpath "$my_dependency.kotlin_gradle"
}
}
task clean(type: Delete) {
delete rootProject.buildDir
}
plugins {
id 'com.android.application'
id 'kotlin-android'
}
android {
compileSdk 31
defaultConfig {
applicationId "ycr.pass"
minSdk versions.minSdk
targetSdk versions.targetSdk
versionCode versions.versionCode
versionName versions.versionName
vectorDrawables {
useSupportLibrary true
}
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_11
targetCompatibility JavaVersion.VERSION_11
}
kotlinOptions {
jvmTarget = '11'
useIR = true
}
buildFeatures {
compose true
}
composeOptions {
kotlinCompilerExtensionVersion compose_version
}
packagingOptions {
resources {
excludes += '/META-INF/{AL2.0,LGPL2.1}'
}
}
}
dependencies {
implementation my_dependency.core_ktx
implementation my_dependency.compose_ui_ui
implementation my_dependency.compose_material
implementation my_dependency.compose_ui_tooling_preview
implementation my_dependency.compose_activity
implementation my_dependency.compose_runtime_livedata
debugImplementation my_dependency.compose_ui_tooling
}

Get Context of a Service

I'm trying to get the context of my Service in order. The service opens up an overlay that draws on other apps. The overlay comes up but if I interact with any of the views, the app crashes and gives this error.
android.view.WindowManager$BadTokenException: Unable to add window -- token null is not valid; is your activity running?
Here is the full error.
android.view.WindowManager$BadTokenException: Unable to add window -- token null is not valid; is your activity running?
at android.view.ViewRootImpl.setView(ViewRootImpl.java:1068)
at android.view.WindowManagerGlobal.addView(WindowManagerGlobal.java:409)
at android.view.WindowManagerImpl.addView(WindowManagerImpl.java:109)
at android.app.Dialog.show(Dialog.java:340)
at android.widget.Spinner$DialogPopup.show(Spinner.java:1146)
at android.widget.Spinner.performClick(Spinner.java:792)
at android.view.View.performClickInternal(View.java:7425)
at android.view.View.access$3600(View.java:810)
at android.view.View$PerformClick.run(View.java:28305)
at android.os.Handler.handleCallback(Handler.java:938)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:223)
at android.app.ActivityThread.main(ActivityThread.java:7656)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:592)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:947)
From what I've been able to determine through Google Search and SO is that the issue is with the context. Below is the code for my Service.
class Dooa: Service() {
private lateinit var floatView: ViewGroup
private lateinit var floatWindowLayoutParams: WindowManager.LayoutParams
private var LAYOUT_TYPE: Int? = null
private lateinit var windowManager: WindowManager
private lateinit var spinnerAccount: Spinner
private lateinit var tvDateAT: TextView
private lateinit var spinnerType: Spinner
private lateinit var etTitle: EditText
private lateinit var etMemo: EditText
private lateinit var spinnerCategory: Spinner
private lateinit var spinnerDebitOrCredit: Spinner
private lateinit var etAmount: EditText
private lateinit var ibSave: ImageButton
private lateinit var ibCancel: ImageButton
private var account: String = "Joint"
private var debitOrCredit: String = "Debit"
private var category: String = ""
private var type: String = "CC"
private var mils: Long = 0
private var balance: String = ""
private var context: Context? = null
private lateinit var db : FirebaseFirestore
override fun onBind(intent: Intent?): IBinder? {
return null
}
override fun onCreate() {
super.onCreate()
}
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
context = MyApp().getContext()
Log.d("blocks", "context: $context")
if (intent != null) {
if (intent.action == START) {
db = FirebaseFirestore.getInstance()
val metrics = applicationContext.resources.displayMetrics
val width = metrics.widthPixels
val height = metrics.heightPixels
windowManager = getSystemService(WINDOW_SERVICE) as WindowManager
val inflator = this.getSystemService(LAYOUT_INFLATER_SERVICE) as LayoutInflater
floatView = inflator.inflate(R.layout.dooa_transaction_card, null) as ViewGroup
spinnerAccount = floatView.findViewById(R.id.spinnerAccount)
tvDateAT = floatView.findViewById(R.id.tvDateAT)
spinnerType = floatView.findViewById(R.id.spinnerType)
etTitle = floatView.findViewById(R.id.etTitle)
etMemo = floatView.findViewById(R.id.etMemo)
spinnerCategory = floatView.findViewById(R.id.spinnerCategory)
spinnerDebitOrCredit = floatView.findViewById(R.id.spinnerDebitOrCredit)
etAmount = floatView.findViewById(R.id.etAmount)
ibSave = floatView.findViewById(R.id.ibSave)
ibCancel = floatView.findViewById(R.id.ibCancel)
//ACCOUNT SPINNER
ArrayAdapter.createFromResource(
this,
R.array.accounts,
R.layout.spinner_item
).also { adapter ->
Log.d("blocks", "AS ArrayAdapter ran")
adapter.setDropDownViewResource(R.layout.spinner_dropdown_item)
spinnerAccount.adapter = adapter
spinnerAccount.setSelection(0)
}
spinnerAccount.onItemSelectedListener = object : AdapterView.OnItemSelectedListener{
override fun onItemSelected(
parent: AdapterView<*>?,
view: View?,
position: Int,
id: Long
) {
val selection = parent?.getItemAtPosition(position)
account = selection.toString()
getDB()
}
override fun onNothingSelected(parent: AdapterView<*>?) {
}
}
tvDateAT.setOnClickListener {
val c = Calendar.getInstance()
val year = c.get(Calendar.YEAR)
val month = c.get(Calendar.MONTH)
val day = c.get(Calendar.DAY_OF_MONTH)
val dpd = DatePickerDialog(
this#Dooa,
{ view, year, monthOfYear, dayOfMonth ->
// Save milliseconds for date picked.
mils = c.timeInMillis
val m = monthOfYear + 1
// Display Selected date in textbox
tvDateAT.text = getString(
R.string.date_picked,
m.toString(),
dayOfMonth.toString(),
year.toString()
)
},
year,
month,
day
)
dpd.show()
}
//TYPE
ArrayAdapter.createFromResource(
this,
R.array.type,
R.layout.spinner_item
).also { adapter ->
adapter.setDropDownViewResource(R.layout.spinner_dropdown_item)
spinnerType.adapter = adapter
spinnerType.setSelection(0)
}
spinnerType.onItemSelectedListener = object : AdapterView.OnItemSelectedListener{
override fun onItemSelected(
parent: AdapterView<*>?,
view: View?,
position: Int,
id: Long
) {
val selection = parent?.getItemAtPosition(position)
type = selection.toString()
}
override fun onNothingSelected(parent: AdapterView<*>?) {
TODO("Not yet implemented")
}
}
etTitle.setOnTouchListener(object : View.OnTouchListener {
override fun onTouch(v: View?, event: MotionEvent?): Boolean {
etTitle.isCursorVisible = true
val updatedFloatParamsFlag = floatWindowLayoutParams
updatedFloatParamsFlag.flags =
WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL or WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
updatedFloatParamsFlag.flags = WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE
windowManager.updateViewLayout(floatView, updatedFloatParamsFlag)
return false
}
})
//CATEGORY
ArrayAdapter.createFromResource(
this,
R.array.category,
R.layout.spinner_item
).also { adapter ->
adapter.setDropDownViewResource(R.layout.spinner_dropdown_item)
spinnerCategory.adapter = adapter
spinnerCategory.setSelection(0)
}
spinnerCategory.onItemSelectedListener = object : AdapterView.OnItemSelectedListener{
override fun onItemSelected(
parent: AdapterView<*>?,
view: View?,
position: Int,
id: Long
) {
val selection = parent?.getItemAtPosition(position)
category = selection.toString()
}
override fun onNothingSelected(parent: AdapterView<*>?) {
TODO("Not yet implemented")
}
}
//DEBIT OR CREDIT
ArrayAdapter.createFromResource(
this,
R.array.debit_or_credit,
R.layout.spinner_item
).also { adapter ->
adapter.setDropDownViewResource(R.layout.spinner_dropdown_item)
spinnerDebitOrCredit.adapter = adapter
spinnerDebitOrCredit.setSelection(0)
}
spinnerDebitOrCredit.onItemSelectedListener = object : AdapterView.OnItemSelectedListener{
override fun onItemSelected(
parent: AdapterView<*>?,
view: View?,
position: Int,
id: Long
) {
val selection = parent?.getItemAtPosition(position)
debitOrCredit = selection.toString()
}
override fun onNothingSelected(parent: AdapterView<*>?) {
TODO("Not yet implemented")
}
}
ibSave.setOnClickListener {
// save all the info.
val date = tvDateAT.text
val memo = etMemo.text
val title = etTitle.text
val amount = etAmount.text
val uid = FirebaseAuth.getInstance().currentUser!!.uid
val serverTS = FieldValue.serverTimestamp()
val collection = account
var nb = 0.0
val newBalance = if (debitOrCredit.contains("Debit")){
nb = (balance.toDouble() - etAmount.text.toString().toDouble())
} else {
nb = (balance.toDouble() + etAmount.text.toString().toDouble())
}
val info = hashMapOf(
"date" to date.toString(),
"type" to type,
"title" to title.toString(),
"memo" to memo.toString(),
"category" to category,
"debitOrCredit" to debitOrCredit,
"amount" to amount.toString(),
"clearReconcile" to "NA",
"mils" to mils,
"timeStamp" to serverTS
)
if (date != "Date"){
if (title?.isNotEmpty() == true && amount?.isNotEmpty() == true){
val dbAccountTransaction = db.collection("Users").document(uid).collection(collection)
dbAccountTransaction.add(info)
.addOnSuccessListener {
db.collection("Users").document(uid).collection(collection).document("balance")
.update("balance", nb.toString())
.addOnSuccessListener {
Toast.makeText(this, "Transaction was saved.", Toast.LENGTH_SHORT).show()
val i = Intent(this, MainActivity::class.java)
startActivity(i)
}
}
.addOnFailureListener{
Toast.makeText(this, "There was an error. Transaction wasn't saved.", Toast.LENGTH_SHORT).show()
}
} else {
Toast.makeText(this, "Please fill out Title and Amount.", Toast.LENGTH_SHORT).show()
}
} else {
Toast.makeText(this, "Please select a date.", Toast.LENGTH_SHORT).show()
}
}
ibCancel.setOnClickListener {
stopSelf()
windowManager.removeView(floatView)
}
LAYOUT_TYPE = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O){
WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY
} else {
WindowManager.LayoutParams.TYPE_TOAST
}
floatWindowLayoutParams = WindowManager.LayoutParams(
(width * 0.55f).toInt(),
(height * 0.55f).toInt(),
LAYOUT_TYPE!!,
WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL,
PixelFormat.TRANSLUCENT
)
floatWindowLayoutParams.gravity = Gravity.CENTER
floatWindowLayoutParams.x = 0
floatWindowLayoutParams.y = 0
windowManager.addView(floatView, floatWindowLayoutParams)
floatView.setOnTouchListener(object : View.OnTouchListener{
val updatedFloatWindowLayoutParam = floatWindowLayoutParams
private var initialX = 0.0
private var initialY = 0.0
private var initialTouchX = 0.0
private var initialTouchY = 0.0
override fun onTouch(v: View?, event: MotionEvent): Boolean {
when (event.action) {
MotionEvent.ACTION_DOWN -> {
initialX = updatedFloatWindowLayoutParam.x.toDouble()
initialY = updatedFloatWindowLayoutParam.y.toDouble()
initialTouchX = event.rawX.toDouble()
initialTouchY = event.rawY.toDouble()
return true
}
MotionEvent.ACTION_MOVE -> {
updatedFloatWindowLayoutParam.x = (initialX + event.rawX - initialTouchX).toInt()
updatedFloatWindowLayoutParam.y = (initialY + event.rawY - initialTouchY).toInt()
windowManager.updateViewLayout(floatView, updatedFloatWindowLayoutParam)
return true
}
}
return false
}
})
spinnerAccount.setOnTouchListener(object : View.OnTouchListener {
override fun onTouch(v: View?, event: MotionEvent?): Boolean {
val updatedFloatParamsFlag = floatWindowLayoutParams
updatedFloatParamsFlag.flags =
WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL or WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
windowManager.updateViewLayout(floatView, updatedFloatParamsFlag)
return false
}
})
}
}
return START_NOT_STICKY
}
private fun getDB() {
try {
db.collection("Users").document(FirebaseAuth.getInstance().currentUser!!.uid)
.collection(account).document("balance")
.get()
.addOnSuccessListener { document ->
if (document != null) {
val balanceResult = StringBuffer()
balanceResult.append(document.data?.getValue("balance"))
balance = balanceResult.toString()
}
}
} catch (e: Exception){
e.printStackTrace()
}
}
override fun onDestroy() {
super.onDestroy()
stopSelf()
windowManager.removeView(floatView)
}
companion object {
var FOREGROUND_SERVICE_ID = 101
var START = "start"
var STOP_ACTION = "stop"
private const val CHANNEL = "default"
}
}
I have tried several ways of getting the context, but it always seems to come back null. I have tried this, this#Dooa, this.applicationContext, I also created a class MyApp to get context that way. it didn't work either. I used this link. Code below.
class MyApp: Application() {
private var context: Context? = null
override fun onCreate() {
super.onCreate()
context = applicationContext
}
fun getContext(): Context? {
return context?.applicationContext
}
}
I've also checked out this answer about Service is a Context, but I still haven't been able to get this to work.
I have tried the code in the onCreate first then I tried the onStartCommand to no avail. What am I missing?
The window pops up, It pops up with a button click, or from a notification, either way if I click on a view, it gives me the error at the top of this question.
You are doing this:
context = MyApp().getContext()
This is definitely wrong. MyApp is an Android component. You are not allowed to instantiate Android components yourself. Only the Android framework can do this (as it also sets up the Context and other important values.
If you want the Service context, just use this (A Service is a Context).
If you want the application context, just use getApplication() (Application is also a Context).

Vapor: route return before all modification done

I have following route:
router.post([Page].self, at: "/fetchStatusOfManagedReleases") { (req, pages) -> Future<[Page]> in
let eventIds = pages.map { $0.events }.flatMap { $0 }.map { $0.id }
return Release.query(on: req).filter(\.fbId ~~ eventIds).all().map { releases in
var result: [Page] = []
for p in pages {
let page = p
var pageEvents: [Event] = []
for e in p.events {
let event = e
if let release = releases.first(where: { $0.fbId == e.id }) {
event.inProgress = release.inProgress
event.purpose = release.purpose
_ = try release.testPrices.query(on:req).all().map { testP in
event.testPrices = testP // <--- this line is not applied
}
} else {
event.inProgress = false
}
pageEvents.append(event)
}
page.events = pageEvents
result.append(page)
}
return result
}
}
Unfortunatelly event.testPrices = testP is not applied, it will e not part of the response. What can I do? At some cases I do not need to postpone "return". How can I dissolve scheduling issue?
I do a ~~ operation on TestPrice also as for Release before.
router.post([Page].self, at: "/fetchStatusOfManagedReleases") { (req, pages) -> Future<[Page]> in
let eventIds = pages.map { $0.events }.flatMap { $0 }.map { $0.id }
return Release.query(on: req).filter(\.fbId ~~ eventIds).all().flatMap { releases in
let releaseInnerIds = releases.map {$0.id}
return TestPrice.query(on: req).filter(\.id ~~ releaseInnerIds).all().map { testPrices in
var result: [Page] = []
for p in pages {
let page = p
var pageEvents: [Event] = []
for e in p.events {
let event = e
if let release = releases.first(where: { $0.fbId == e.id }) {
event.inProgress = release.inProgress
event.purpose = release.purpose
event.testPrices = testPrices.compactMap({testPrice in
if testPrice.release.parentID == release.id {
return testPrice
} else {
return nil
}
})
} else {
event.inProgress = false
}
pageEvents.append(event)
}
page.events = pageEvents
result.append(page)
}
return result
}
}
}

Android LiveData: Unable to receive updates on Observable?

I am using android Architecture Component LiveData to notify UI using Observable LiveData but it is not getting triggered. below is the code snippet.
AuthRepository
class AuthRepository(private val repository:Avails) {
fun auth(mobile: String): LiveData<Boolean>{
var data: MutableLiveData<Boolean> = MutableLiveData()
repository.auth(prepareHeaders(), AuthRequest(mobile))
.enqueue(object : Callback<AuthResponse>{
override fun onResponse(call: Call<AuthResponse>, response: Response<AuthResponse>) {
if(response.isSuccessful){
data.value = true
}
}
override fun onFailure(call: Call<AuthResponse>, t: Throwable) {
data.value = false
}
})
return data
}
}
LoginViewModel
class LoginViewModel(private val repository: AuthRepository) : ViewModel() {
var _showOtpScreen: MutableLiveData<Boolean> = MutableLiveData()
fun auth(mobile: String){
_showOtpScreen.value = repository.auth(mobile).value
}
}
LoginFragment
class LoginFragment : Fragment() {
private lateinit var loginViewModel: LoginViewModel
companion object {
private const val sTag: String = "LoginFragment"
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
val authRepository = AuthRepository(AvailsClient.retrofit.create(Avails::class.java))
loginViewModel = ViewModelProviders.of(this,LoginViewModelFactory(authRepository)).get(LoginViewModel::class.java)
loginViewModel._showOtpScreen.observe(this, Observer {
if(it != null){
if(it){
Log.e(sTag,"OTP RECEIVED")
findNavController().navigate(R.id.action_loginFragment_to_verifyOtpFragment)
}else{
Log.e(sTag,"Failed to get OTP")
}
}
})
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?): View? {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_login, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
btnContinue.setOnClickListener {
loginViewModel.auth(edtPhoneNumber.text.toString())
}
}
}
Above code is not able to Observe _showOtpScreen it is only called once with null value and never gets called again when service call completes.
Event wrapper is the solution for above problem.

Resources