Threads notification badge UI

This commit is contained in:
ariskotsomitopoulos 2021-12-03 11:30:30 +00:00
parent 0241d66f8e
commit d1bb96cec0
8 changed files with 183 additions and 14 deletions

View file

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<dimen name="menu_item_ripple_size">28dp</dimen>
</resources>

View file

@ -41,4 +41,8 @@
<!-- Navigation Drawer -->
<dimen name="navigation_drawer_max_width">320dp</dimen>
<dimen name="menu_item_icon_size">24dp</dimen>
<dimen name="menu_item_size">48dp</dimen>
<dimen name="menu_item_ripple_size">48dp</dimen>
</resources>

View file

@ -680,17 +680,17 @@ class RoomDetailViewModel @AssistedInject constructor(
}
} else {
when (itemId) {
R.id.timeline_setting -> true
R.id.invite -> state.canInvite
R.id.open_matrix_apps -> true
R.id.voice_call -> state.isWebRTCCallOptionAvailable()
R.id.video_call -> state.isWebRTCCallOptionAvailable() || state.jitsiState.confId == null || state.jitsiState.hasJoined
R.id.timeline_setting -> true
R.id.invite -> state.canInvite
R.id.open_matrix_apps -> true
R.id.voice_call -> state.isWebRTCCallOptionAvailable()
R.id.video_call -> state.isWebRTCCallOptionAvailable() || state.jitsiState.confId == null || state.jitsiState.hasJoined
// Show Join conference button only if there is an active conf id not joined. Otherwise fallback to default video disabled. ^
R.id.join_conference -> !state.isWebRTCCallOptionAvailable() && state.jitsiState.confId != null && !state.jitsiState.hasJoined
R.id.search -> true
R.id.threads -> BuildConfig.THREADING_ENABLED
R.id.dev_tools -> vectorPreferences.developerMode()
else -> false
R.id.join_conference -> !state.isWebRTCCallOptionAvailable() && state.jitsiState.confId != null && !state.jitsiState.hasJoined
R.id.search -> true
R.id.menu_timeline_thread_list -> BuildConfig.THREADING_ENABLED
R.id.dev_tools -> vectorPreferences.developerMode()
else -> false
}
}
}

View file

@ -36,12 +36,14 @@ import android.view.MenuItem
import android.view.View
import android.view.ViewGroup
import android.view.inputmethod.EditorInfo
import android.widget.FrameLayout
import android.widget.ImageView
import android.widget.TextView
import android.widget.Toast
import androidx.annotation.DrawableRes
import androidx.annotation.StringRes
import androidx.core.content.ContextCompat
import androidx.core.graphics.drawable.DrawableCompat
import androidx.core.net.toUri
import androidx.core.text.buildSpannedString
import androidx.core.text.toSpannable
@ -61,6 +63,7 @@ import com.airbnb.epoxy.OnModelBuildFinishedListener
import com.airbnb.epoxy.addGlidePreloader
import com.airbnb.epoxy.glidePreloader
import com.airbnb.mvrx.Mavericks
import com.airbnb.mvrx.activityViewModel
import com.airbnb.mvrx.args
import com.airbnb.mvrx.fragmentViewModel
import com.airbnb.mvrx.withState
@ -133,6 +136,7 @@ import im.vector.app.features.command.Command
import im.vector.app.features.crypto.keysbackup.restore.KeysBackupRestoreActivity
import im.vector.app.features.crypto.verification.VerificationBottomSheet
import im.vector.app.features.home.AvatarRenderer
import im.vector.app.features.home.UnreadMessagesSharedViewModel
import im.vector.app.features.home.room.detail.arguments.TimelineArgs
import im.vector.app.features.home.room.detail.composer.SendMode
import im.vector.app.features.home.room.detail.composer.TextComposerAction
@ -285,6 +289,7 @@ class TimelineFragment @Inject constructor(
private val roomDetailViewModel: RoomDetailViewModel by fragmentViewModel()
private val textComposerViewModel: TextComposerViewModel by fragmentViewModel()
private val debouncer = Debouncer(createUIHandler())
private lateinit var scrollOnNewMessageCallback: ScrollOnNewMessageCallback
@ -907,6 +912,13 @@ class TimelineFragment @Inject constructor(
(joinConfItem.actionView as? JoinConferenceView)?.onJoinClicked = {
roomDetailViewModel.handle(RoomDetailAction.JoinJitsiCall)
}
// Custom thread notification menu item
menu.findItem(R.id.menu_timeline_thread_list)?.let { menuItem ->
menuItem.actionView.setOnClickListener {
onOptionsItemSelected(menuItem)
}
}
}
override fun onPrepareOptionsMenu(menu: Menu) {
@ -946,6 +958,10 @@ class TimelineFragment @Inject constructor(
actionView.findViewById<TextView>(R.id.cart_badge).setTextOrHide("$widgetsCount")
matrixAppsMenuItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS)
}
// Handle custom threads badge notification
updateMenuThreadNotificationBadge(menu, state)
}
}
@ -971,7 +987,7 @@ class TimelineFragment @Inject constructor(
callActionsHandler.onVideoCallClicked()
true
}
R.id.threads -> {
R.id.menu_timeline_thread_list -> {
navigateToThreadList()
true
}
@ -1007,6 +1023,29 @@ class TimelineFragment @Inject constructor(
}
}
/**
* Update menu thread notification badge appropriately
*/
private fun updateMenuThreadNotificationBadge(menu: Menu, state: RoomDetailViewState) {
val menuThreadList = menu.findItem(R.id.menu_timeline_thread_list).actionView
val badgeFrameLayout = menuThreadList.findViewById<FrameLayout>(R.id.threadNotificationBadgeFrameLayout)
val badgeTextView = menuThreadList.findViewById<TextView>(R.id.threadNotificationBadgeTextView)
val unreadThreadMessages = 18 + state.pushCounter
val userIsMentioned = true
if (unreadThreadMessages > 0) {
badgeFrameLayout.isVisible = true
badgeTextView.text = unreadThreadMessages.toString()
val badgeDrawable = DrawableCompat.wrap(badgeFrameLayout.background)
val color = ContextCompat.getColor(requireContext(), if (userIsMentioned) R.color.palette_vermilion else R.color.palette_gray_200)
DrawableCompat.setTint(badgeDrawable, color)
badgeFrameLayout.background = badgeDrawable
} else {
badgeFrameLayout.isVisible = false
}
}
/**
* View and highlight the original root thread message in the main timeline
*/

View file

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<!-- "background shadow" -->
<!-- background color -->
<item>
<shape android:shape="rectangle">
<corners android:radius="15dp"/>
<solid android:color="?vctr_content_secondary"/>
</shape>
</item>
</layer-list>

View file

@ -0,0 +1,58 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="@dimen/menu_item_size"
android:layout_height="@dimen/menu_item_size">
<FrameLayout
android:layout_width="@dimen/menu_item_ripple_size"
android:layout_height="@dimen/menu_item_ripple_size"
android:layout_gravity="center"
android:background="?attr/selectableItemBackgroundBorderless"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<ImageView
android:layout_width="@dimen/menu_item_icon_size"
android:layout_height="@dimen/menu_item_icon_size"
android:layout_gravity="center"
android:src="@drawable/ic_thread_menu_item"
tools:ignore="ContentDescription"
app:tint="?colorPrimary" />
<FrameLayout
android:id="@+id/threadNotificationBadgeFrameLayout"
android:layout_width="wrap_content"
android:layout_height="15dp"
android:minWidth="15dp"
android:paddingStart="2dp"
android:paddingEnd="2dp"
android:layout_gravity="top|end"
android:layout_marginTop="1dp"
android:backgroundTint="@color/palette_gray_200"
android:background="@drawable/notification_badge"
android:visibility="visible"
tools:visibility="visible">
<TextView
android:id="@+id/threadNotificationBadgeTextView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingEnd="2dp"
android:paddingStart="2dp"
android:gravity="center"
android:layout_gravity="center"
android:textColor="@color/palette_white"
android:maxLength="3"
android:textStyle="bold"
android:textSize="10sp"
tools:text="99" />
</FrameLayout>
</FrameLayout>
</androidx.constraintlayout.widget.ConstraintLayout>

View file

@ -0,0 +1,53 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/notificationConstraintLayout"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:visibility="gone"
tools:visibility="visible">
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/notificationImageView"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:clickable="true"
android:focusable="true"
android:src="@drawable/ic_thread_menu_item" />
<ImageView
android:id="@+id/notificationBadgeImageView"
app:layout_constraintEnd_toEndOf="@+id/notificationImageView"
app:layout_constraintTop_toTopOf="@+id/notificationImageView"
android:layout_width="15dp"
android:layout_height="15dp"
android:src="@drawable/notification_badge"
android:visibility="gone"
tools:ignore="ContentDescription"
tools:visibility="visible" />
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/notificationBadgeTextView"
style="@style/Widget.Vector.TextView.Subtitle"
app:layout_constraintBottom_toBottomOf="@+id/notificationBadgeImageView"
app:layout_constraintEnd_toEndOf="@+id/notificationBadgeImageView"
app:layout_constraintStart_toStartOf="@+id/notificationBadgeImageView"
app:layout_constraintTop_toTopOf="@+id/notificationBadgeImageView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center"
android:textSize="11sp"
android:visibility="gone"
tools:text="8"
tools:visibility="visible" />
</androidx.constraintlayout.widget.ConstraintLayout>

View file

@ -38,14 +38,13 @@
tools:visible="true" />
<item
android:id="@+id/threads"
android:icon="@drawable/ic_thread_menu_item"
android:id="@+id/menu_timeline_thread_list"
android:title="@string/action_view_threads"
android:visible="false"
app:iconTint="?colorPrimary"
app:actionLayout="@layout/view_thread_notification_badge"
app:showAsAction="always"
tools:visible="true" />
<item
android:id="@+id/join_conference"
android:title="@string/join"