mirror of
https://github.com/sudoxnym/habitica-android.git
synced 2026-04-14 19:56:32 +00:00
Fix chat layout issues on some android 15 devices
This commit is contained in:
parent
6125f90577
commit
33bb40370b
11 changed files with 129 additions and 54 deletions
|
|
@ -32,7 +32,6 @@
|
|||
<activity
|
||||
android:name=".ui.activities.MainActivity"
|
||||
android:theme="@style/LaunchAppTheme"
|
||||
android:windowSoftInputMode="stateHidden|adjustResize"
|
||||
android:configChanges="screenSize | smallestScreenSize | screenLayout"
|
||||
android:exported="true">
|
||||
<nav-graph android:value="@navigation/navigation" />
|
||||
|
|
|
|||
|
|
@ -1,14 +1,16 @@
|
|||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:orientation="vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:orientation="vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
<com.habitrpg.android.habitica.ui.helpers.RecyclerViewEmptySupport
|
||||
android:id="@+id/recyclerView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="1"/>
|
||||
android:id="@+id/recyclerView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="1"
|
||||
app:windowInsetBottom="false" />
|
||||
<com.habitrpg.android.habitica.ui.views.social.ChatBarView
|
||||
android:id="@+id/chatBarView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content" />
|
||||
android:id="@+id/chatBarView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content" />
|
||||
</LinearLayout>
|
||||
|
|
|
|||
|
|
@ -1,14 +1,16 @@
|
|||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:orientation="vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
<com.habitrpg.android.habitica.ui.helpers.RecyclerViewEmptySupport
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:orientation="vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
<com.habitrpg.android.habitica.ui.helpers.RecyclerViewEmptySupport
|
||||
android:id="@+id/recyclerView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="1"/>
|
||||
android:layout_weight="1"
|
||||
app:windowInsetBottom="false" />
|
||||
<com.habitrpg.android.habitica.ui.views.social.ChatBarView
|
||||
android:id="@+id/chatBarView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"/>
|
||||
</LinearLayout>
|
||||
android:id="@+id/chatBarView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content" />
|
||||
</LinearLayout>
|
||||
|
|
|
|||
|
|
@ -150,4 +150,10 @@
|
|||
<declare-styleable name="HabiticaScrollView">
|
||||
<attr name="skipDirectchild" format="boolean" />
|
||||
</declare-styleable>
|
||||
<declare-styleable name="RecyclerViewEmptySupport">
|
||||
<attr name="windowInsetTop" format="boolean" />
|
||||
<attr name="windowInsetStart" format="boolean" />
|
||||
<attr name="windowInsetEnd" format="boolean" />
|
||||
<attr name="windowInsetBottom" format="boolean" />
|
||||
</declare-styleable>
|
||||
</resources>
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -145,13 +145,6 @@ class InboxMessageListFragment : BaseMainFragment<FragmentInboxMessageListBindin
|
|||
super.onResume()
|
||||
}
|
||||
|
||||
override fun onAttach(context: Context) {
|
||||
view?.invalidate()
|
||||
view?.forceLayout()
|
||||
|
||||
super.onAttach(context)
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
socialRepository.close()
|
||||
super.onDestroy()
|
||||
|
|
|
|||
|
|
@ -65,14 +65,12 @@ class PartyFragment : BaseMainFragment<FragmentViewpagerBinding>() {
|
|||
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<FragmentViewpagerBinding>() {
|
|||
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"
|
||||
|
|
|
|||
|
|
@ -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<OnImeVisibilityChangedListener>()
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Reference in a new issue