diff --git a/Habitica/AndroidManifest.xml b/Habitica/AndroidManifest.xml index d980ffcb6..ae0e89131 100644 --- a/Habitica/AndroidManifest.xml +++ b/Habitica/AndroidManifest.xml @@ -32,7 +32,6 @@ diff --git a/Habitica/res/layout/fragment_chat.xml b/Habitica/res/layout/fragment_chat.xml index 915102d99..10be67354 100644 --- a/Habitica/res/layout/fragment_chat.xml +++ b/Habitica/res/layout/fragment_chat.xml @@ -1,14 +1,16 @@ + xmlns:app="http://schemas.android.com/apk/res-auto" + android:orientation="vertical" + android:layout_width="match_parent" + android:layout_height="match_parent"> + android:id="@+id/recyclerView" + android:layout_width="match_parent" + android:layout_height="0dp" + android:layout_weight="1" + app:windowInsetBottom="false" /> + android:id="@+id/chatBarView" + android:layout_width="match_parent" + android:layout_height="wrap_content" /> diff --git a/Habitica/res/layout/fragment_inbox_message_list.xml b/Habitica/res/layout/fragment_inbox_message_list.xml index 52f2d94b7..426fbce30 100644 --- a/Habitica/res/layout/fragment_inbox_message_list.xml +++ b/Habitica/res/layout/fragment_inbox_message_list.xml @@ -1,14 +1,16 @@ - + + android:layout_weight="1" + app:windowInsetBottom="false" /> - \ No newline at end of file + android:id="@+id/chatBarView" + android:layout_width="match_parent" + android:layout_height="wrap_content" /> + diff --git a/Habitica/res/values/attrs.xml b/Habitica/res/values/attrs.xml index 623938165..6249122c7 100644 --- a/Habitica/res/values/attrs.xml +++ b/Habitica/res/values/attrs.xml @@ -150,4 +150,10 @@ + + + + + + diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/BaseActivity.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/BaseActivity.kt index befde2482..99f084992 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/BaseActivity.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/BaseActivity.kt @@ -42,6 +42,7 @@ import com.habitrpg.android.habitica.helpers.EventCategory import com.habitrpg.android.habitica.helpers.HitType import com.habitrpg.android.habitica.helpers.NotificationsManager import com.habitrpg.android.habitica.interactors.ShowNotificationInteractor +import com.habitrpg.android.habitica.ui.helpers.KeyboardUtil import com.habitrpg.android.habitica.ui.helpers.ToolbarColorHelper import com.habitrpg.android.habitica.ui.views.dialogs.HabiticaAlertDialog import com.habitrpg.common.habitica.extensions.getThemeColor @@ -145,8 +146,11 @@ abstract class BaseActivity : AppCompatActivity() { ViewCompat.setOnApplyWindowInsetsListener(appbar) { v, windowInsets -> val insets = windowInsets.getInsets( WindowInsetsCompat.Type.systemBars() - + WindowInsetsCompat.Type.displayCutout() + + WindowInsetsCompat.Type.displayCutout() ) + val isImeVisible = windowInsets.isVisible(WindowInsetsCompat.Type.ime()) + val imeHeight = windowInsets.getInsets(WindowInsetsCompat.Type.ime()).bottom + KeyboardUtil.updatePaddingForIme(isImeVisible, imeHeight, insets) v.updatePadding(top = insets.top + paddingTop) (v as AppBarLayout).children.forEach { if (it !is TabLayout) { diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/social/InboxMessageListFragment.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/social/InboxMessageListFragment.kt index ac9e6ad71..19d5f18e7 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/social/InboxMessageListFragment.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/social/InboxMessageListFragment.kt @@ -145,13 +145,6 @@ class InboxMessageListFragment : BaseMainFragment() { updateGroupUI(it) } - binding?.viewPager?.currentItem = 0 - setViewPagerAdapter() arguments?.let { val args = PartyFragmentArgs.fromBundle(it) binding?.viewPager?.post { - binding?.viewPager?.currentItem = args.tabToOpen + binding?.viewPager?.currentItem = savedInstanceState?.getInt("tab") ?: args.tabToOpen } if (args.partyID?.isNotBlank() == true) { viewModel.setGroupID(args.partyID ?: "") @@ -84,6 +82,11 @@ class PartyFragment : BaseMainFragment() { viewModel.retrieveGroup {} } + override fun onSaveInstanceState(outState: Bundle) { + super.onSaveInstanceState(outState) + outState.putInt("tab", binding?.viewPager?.currentItem ?: 0) + } + override fun onResume() { if (viewModel.isLeader) { this.tutorialStepIdentifier = "party" diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/helpers/KeyboardUtil.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/helpers/KeyboardUtil.kt index 8204d2722..16d94f545 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/helpers/KeyboardUtil.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/helpers/KeyboardUtil.kt @@ -17,10 +17,18 @@ package com.habitrpg.android.habitica.ui.helpers */ import android.app.Activity +import android.util.Log import android.view.inputmethod.InputMethodManager +import androidx.core.graphics.Insets + +interface OnImeVisibilityChangedListener { + fun onImeVisibilityChanged(visible: Boolean, height: Int, safeInsets: Insets) +} class KeyboardUtil { companion object { + private val imeListeners = mutableListOf() + fun dismissKeyboard(act: Activity?) { if (act != null && act.currentFocus != null) { val inputMethodManager = @@ -28,6 +36,31 @@ class KeyboardUtil { inputMethodManager?.hideSoftInputFromWindow(act.currentFocus?.windowToken, 0) } } + + private var lastVisible = false + private var lastHeight = 0 + fun updatePaddingForIme(isVisible: Boolean, height: Int, safeInsets: Insets) { + if (lastVisible == isVisible && lastHeight == height) { + return + } + lastVisible = isVisible + lastHeight = height + Log.e("KeyboardUtil", "updatePaddingForIme: isVisible = $isVisible, height = $height") + for (listener in imeListeners) { + Log.e("KeyboardUtil", "updatePaddingForIme: listener = $listener") + listener.onImeVisibilityChanged(isVisible, height, safeInsets) + } + } + + fun addImeVisibilityListener(listener: OnImeVisibilityChangedListener) { + Log.e("KeyboardUtil", "addImeVisibilityListener: listener = $listener") + imeListeners.add(listener) + } + + fun removeImeVisibilityListener(listener: OnImeVisibilityChangedListener) { + Log.e("KeyboardUtil", "removeImeVisibilityListener: listener = $listener") + imeListeners.remove(listener) + } } } diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/helpers/RecyclerViewEmptySupport.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/helpers/RecyclerViewEmptySupport.kt index 483566d48..7dbe1563b 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/helpers/RecyclerViewEmptySupport.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/helpers/RecyclerViewEmptySupport.kt @@ -6,6 +6,7 @@ import androidx.core.view.ViewCompat import androidx.core.view.WindowInsetsCompat import androidx.core.view.updatePadding import androidx.recyclerview.widget.RecyclerView +import com.habitrpg.android.habitica.R import com.habitrpg.android.habitica.extensions.consumeWindowInsetsAbove30 import com.habitrpg.common.habitica.helpers.EmptyItem import com.habitrpg.common.habitica.helpers.RecyclerViewState @@ -50,22 +51,35 @@ constructor( private var actualAdapter: Adapter<*>? = null private val emptyAdapter = RecyclerViewStateAdapter() + private var windowInsetTop = false + private var windowInsetBottom = true + private var windowInsetStart = true + private var windowInsetEnd = true + init { + context.theme?.obtainStyledAttributes(attrs, R.styleable.RecyclerViewEmptySupport, 0, 0)?.let { + windowInsetTop = it.getBoolean(R.styleable.RecyclerViewEmptySupport_windowInsetTop, false) + windowInsetBottom = it.getBoolean(R.styleable.RecyclerViewEmptySupport_windowInsetBottom, true) + windowInsetStart = it.getBoolean(R.styleable.RecyclerViewEmptySupport_windowInsetStart, true) + windowInsetEnd = it.getBoolean(R.styleable.RecyclerViewEmptySupport_windowInsetEnd, true) + } + clipToPadding = false val topPadding = paddingTop val bottomPadding = paddingBottom val leftPadding = paddingLeft val rightPadding = paddingRight + ViewCompat.setOnApplyWindowInsetsListener(this) { v, insets -> val bars = insets.getInsets( WindowInsetsCompat.Type.systemBars() or WindowInsetsCompat.Type.displayCutout() ) v.updatePadding( - left = bars.left + leftPadding, - top = topPadding, - right = bars.right + rightPadding, - bottom = bars.bottom + bottomPadding, + left = (if (windowInsetStart) bars.left else 0) + leftPadding, + top = (if (windowInsetTop) bars.top else 0) + topPadding, + right = (if (windowInsetEnd) bars.right else 0) + rightPadding, + bottom = (if (windowInsetBottom) bars.bottom else 0) + bottomPadding, ) consumeWindowInsetsAbove30(insets) } diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/social/ChatBarView.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/social/ChatBarView.kt index af66a2b8b..4d8fcce21 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/social/ChatBarView.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/social/ChatBarView.kt @@ -3,12 +3,14 @@ package com.habitrpg.android.habitica.ui.views.social import android.content.Context import android.content.res.Configuration import android.util.AttributeSet +import android.util.Log import android.view.View import android.view.inputmethod.EditorInfo import android.view.inputmethod.InputMethodManager import android.widget.LinearLayout import android.widget.TextView import androidx.core.content.ContextCompat +import androidx.core.graphics.Insets import androidx.core.view.ViewCompat import androidx.core.view.WindowInsetsCompat import androidx.core.view.updatePadding @@ -19,11 +21,13 @@ import com.habitrpg.android.habitica.extensions.consumeWindowInsetsAbove30 import com.habitrpg.android.habitica.models.social.ChatMessage import com.habitrpg.android.habitica.ui.helpers.AutocompleteAdapter import com.habitrpg.android.habitica.ui.helpers.AutocompleteTokenizer +import com.habitrpg.android.habitica.ui.helpers.KeyboardUtil +import com.habitrpg.android.habitica.ui.helpers.OnImeVisibilityChangedListener import com.habitrpg.common.habitica.extensions.getThemeColor import com.habitrpg.common.habitica.extensions.layoutInflater import com.habitrpg.common.habitica.helpers.MainNavigationController -class ChatBarView : LinearLayout { +class ChatBarView : LinearLayout, OnImeVisibilityChangedListener { var hasAcceptedGuidelines: Boolean = false set(value) { field = value @@ -62,6 +66,9 @@ class ChatBarView : LinearLayout { get() = binding.chatEditText.text.toString() set(value) = binding.chatEditText.setText(value, TextView.BufferType.EDITABLE) + private var safeInsets: Insets = Insets.NONE + private var imeHeight: Int = 0 + constructor(context: Context) : super(context) { setupView() } @@ -108,19 +115,13 @@ class ChatBarView : LinearLayout { } } - override fun onAttachedToWindow() { - super.onAttachedToWindow() + override fun onWindowVisibilityChanged(visibility: Int) { + super.onWindowVisibilityChanged(visibility) - ViewCompat.setOnApplyWindowInsetsListener(this) { _, insets -> - val imeVisible = insets.isVisible(WindowInsetsCompat.Type.ime()) - val imeHeight = insets.getInsets(WindowInsetsCompat.Type.ime()).bottom - val safePadding = insets.getInsets(WindowInsetsCompat.Type.systemBars() - or WindowInsetsCompat.Type.displayCutout()) - updatePadding( - left = safePadding.left, - right = safePadding.right, - bottom = if (imeVisible) imeHeight else safePadding.bottom) - consumeWindowInsetsAbove30(insets) + if (visibility == VISIBLE) { + KeyboardUtil.addImeVisibilityListener(this) + } else { + KeyboardUtil.removeImeVisibilityListener(this) } } @@ -160,4 +161,22 @@ class ChatBarView : LinearLayout { sendAction?.invoke(chatText) } } + + override fun onImeVisibilityChanged(visible: Boolean, height: Int, safeInsets: Insets) { + this.safeInsets = safeInsets + imeHeight = if (visible) { + height + } else { + 0 + } + applyAllPadding() + } + + private fun applyAllPadding() { + Log.e("ChatBarView", "applyAllPadding: safeInsets = $safeInsets, imeHeight = $imeHeight") + updatePadding( + left = safeInsets.left, + right = safeInsets.right, + bottom = if (imeHeight > 0) imeHeight else safeInsets.bottom) + } } diff --git a/fastlane/changelog.txt b/fastlane/changelog.txt index 223fa34a1..94f3beac6 100644 --- a/fastlane/changelog.txt +++ b/fastlane/changelog.txt @@ -1,6 +1,6 @@ -New in 4.6.0 -- Improvements to reminder scheduling to help them appear on time -- Reminders should no longer appear if a task isn't due that day -- Your avatar now shows at the top of the screen when choosing equipment, letting you see how you look as you try gear on! -- We made it easier to choose what to do with tasks from broken Challenges so they don't get stuck on your task list -- You'll now be able to leave your Party if you're participating in an active Quest +New in 4.7.2 +- Upgraded to the latest Google Sign In authentication standards +- Implemented full edge-to-edge display functionality on Android 11+ devices +- Fixed some issues where the text box in chat wasn't adjusting properly +- More support for landscape mode +- Various other bug fixes and improvements