diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/day/tabs/common/DiaryCircumstancesTextView.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/day/tabs/common/DiaryCircumstancesTextView.kt index 11db3cab18d1285d54c2c1d70cd85381ec0f67e5..012c1a42dbf09ae0a60976576986b0005ff07bb8 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/day/tabs/common/DiaryCircumstancesTextView.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/day/tabs/common/DiaryCircumstancesTextView.kt @@ -36,6 +36,10 @@ class DiaryCircumstancesTextView @JvmOverloads constructor( } imeOptions = EditorInfo.IME_ACTION_DONE setRawInputType(InputType.TYPE_CLASS_TEXT) + // When the user entered something and puts the app into the background + viewTreeObserver.addOnWindowFocusChangeListener { + notifyTextChanged(text.toString()) + } } infoButton = findViewById(R.id.info_button) } diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/lists/modular/mods/SavedStateMod.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/lists/modular/mods/SavedStateMod.kt index 9356a9ad764c8dce5fcd135163f31aee7c7f0c9e..a102f889008d292a250ae1d81aaf3c0e4e1bac8b 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/lists/modular/mods/SavedStateMod.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/lists/modular/mods/SavedStateMod.kt @@ -2,13 +2,10 @@ package de.rki.coronawarnapp.util.lists.modular.mods import android.os.Parcelable import androidx.annotation.Keep -import androidx.fragment.app.Fragment -import androidx.fragment.app.findFragment import androidx.lifecycle.Lifecycle -import androidx.lifecycle.LifecycleObserver -import androidx.lifecycle.OnLifecycleEvent import androidx.recyclerview.widget.RecyclerView import de.rki.coronawarnapp.util.lists.modular.ModularAdapter +import de.rki.coronawarnapp.util.ui.addLifecycleEventCallback import timber.log.Timber @Keep @@ -27,38 +24,25 @@ class SavedStateMod<T : ModularAdapter.VH> : } override fun onAttachedToRecyclerView(recyclerView: RecyclerView) { - val hostLifecycle = recyclerView.hostLifecycle ?: return - - val observer = object : LifecycleObserver { - @OnLifecycleEvent(Lifecycle.Event.ON_STOP) - fun onStop() { - savedStates.clear() - - getAllViewHolders(recyclerView).filterIsInstance<StateSavingVH>().forEach { vh -> - val key = vh.savedStateKey - val state = vh.onSaveState() - if (key != null && state != null) { - savedStates[key] = state - } + recyclerView.addLifecycleEventCallback(type = Lifecycle.Event.ON_STOP) { + savedStates.clear() + + getAllViewHolders(recyclerView).filterIsInstance<StateSavingVH>().forEach { vh -> + val key = vh.savedStateKey + val state = vh.onSaveState() + if (key != null && state != null) { + savedStates[key] = state } - - hostLifecycle.removeObserver(this) } + + true } - hostLifecycle.addObserver(observer) } override fun onDetachedFromRecyclerView(recyclerView: RecyclerView) { // NOOP } - private val RecyclerView.hostLifecycle: Lifecycle? - get() = try { - findFragment<Fragment>().viewLifecycleOwner.lifecycle - } catch (e: Exception) { - null - } - private fun getAllViewHolders(recyclerView: RecyclerView): List<RecyclerView.ViewHolder> = try { (0..recyclerView.childCount) .mapNotNull { recyclerView.getChildAt(it) } diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/ui/ViewExtensions.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/ui/ViewExtensions.kt index 285a6d9a16895523b3da36049e06a4e164940cad..9acd45a081125e4fe25a06bfb1e4a6f04efe31e7 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/ui/ViewExtensions.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/ui/ViewExtensions.kt @@ -2,7 +2,14 @@ package de.rki.coronawarnapp.util.ui import android.view.View import androidx.databinding.BindingAdapter +import androidx.fragment.app.Fragment +import androidx.fragment.app.findFragment +import androidx.lifecycle.Lifecycle +import androidx.lifecycle.LifecycleObserver +import androidx.lifecycle.LifecycleOwner +import androidx.lifecycle.OnLifecycleEvent import de.rki.coronawarnapp.util.recyclerview.ThrottledClickListener +import timber.log.Timber @BindingAdapter("gone") fun View.setGone(gone: Boolean) { @@ -16,3 +23,40 @@ fun View.setInvisible(invisible: Boolean) { fun View.setOnClickListenerThrottled(interval: Long = 300L, listenerBlock: (View) -> Unit) = setOnClickListener(ThrottledClickListener(interval, listenerBlock)) + +val View.hostLifecycle: Lifecycle? + get() = try { + findFragment<Fragment>().viewLifecycleOwner.lifecycle + } catch (e: Exception) { + Timber.v("Couldn't find viewLifecycleOwner for %s", this) + null + } + +/** + * Allows your view to receive callbacks from the host Fragment's lifecycle + * Your callback is invoked when the owning Fragment/Activity receives the specified event state. + * + * @param callback returns true if it should be consumed (one-time callback), or false if it was to stay registered. + * + * @return true if the callback has been added. Otherwise returns false, + * i.e. if the view doesn't have a viewLifecycleOwner due to not being attached. + */ +fun View.addLifecycleEventCallback( + type: Lifecycle.Event, + callback: () -> Boolean +): Boolean { + val hostLifecycle = hostLifecycle ?: return false + + val observer = object : LifecycleObserver { + @OnLifecycleEvent(Lifecycle.Event.ON_ANY) + fun onLifecycleEvent(owner: LifecycleOwner, event: Lifecycle.Event) { + if (event != type) return + Timber.v("%s triggered %s for %s", owner, event, this@addLifecycleEventCallback) + val consumed = callback() + if (consumed) hostLifecycle.removeObserver(this) + } + } + + hostLifecycle.addObserver(observer) + return true +}