Mute/unmute chat room notifications, using 3-dots menu even for basic chat rooms, updated icons

This commit is contained in:
Sylvain Berfini 2022-05-24 17:07:58 +02:00
parent b4fb169e19
commit cc36a18d2c
16 changed files with 171 additions and 56 deletions

View file

@ -14,11 +14,13 @@ Group changes to describe their impact on the project, as follows:
### Added
- Conference creation with scheduling, video, different layouts, showing who is speaking and who is muted, etc...
- Chat rooms can be individually muted (no notification when receiving a chat message)
### Changed
- In-call views have been re-designed
- Improved how call logs are handled to improve performances
- Improved how contact avatars are generated
- 3-dots menu even for basic chat rooms
### Fixed
- Multiple file download attempt from the same chat bubble at the same time needed app restart to properly download each file
@ -27,6 +29,25 @@ Group changes to describe their impact on the project, as follows:
- Show service notification sooner to prevent crash if Core creation takes too long
- Trying to keep the preferred driver (OpenSLES / AAudio) when switching device
## [4.6.9] - 2022-05-30
### Fixed
- ANR when screen turns OFF/ON while app is in foreground
- Crash due to missing CoreContext instance in TelecomManager service
- One-to-One encrypted chat room creation if it already exists
- Crash if ConnectionService feature isn't supported by the device
### Changed
- Updated translations from Weblate
- Improved audio devices logs
## [4.6.8] - 2022-05-23
### Fixed
- Crash due to missing CoreContext in CoreService
- Crash in BootReceiver if auto start is disabled
- Other crashes
## [4.6.7] - 2022-05-04
### Changed

View file

@ -216,7 +216,7 @@ dependencies {
implementation "androidx.slidingpanelayout:slidingpanelayout:1.2.0"
implementation "androidx.window:window:1.0.0"
implementation 'androidx.constraintlayout:constraintlayout:2.1.3'
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
implementation "androidx.gridlayout:gridlayout:1.0.0"
implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0'
implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.4.1'

View file

@ -561,10 +561,6 @@ class DetailChatRoomFragment : MasterFragment<ChatRoomDetailFragmentBinding, Cha
showPopupMenu(chatRoom)
}
binding.setEditClickListener {
enterEditionMode()
}
binding.setSecurityIconClickListener {
showParticipantsDevices()
}
@ -853,32 +849,50 @@ class DetailChatRoomFragment : MasterFragment<ChatRoomDetailFragmentBinding, Cha
)
val itemSize = AppUtils.getDimension(R.dimen.chat_room_popup_item_height).toInt()
var totalSize = itemSize * 4
var totalSize = itemSize * 6
if (!viewModel.encryptedChatRoom) {
val notificationsTurnedOff = viewModel.areNotificationsMuted()
if (notificationsTurnedOff) {
popupView.muteHidden = true
totalSize -= itemSize
} else {
popupView.unmuteHidden = true
totalSize -= itemSize
}
if (viewModel.basicChatRoom) {
popupView.groupInfoHidden = true
totalSize -= itemSize
popupView.devicesHidden = true
totalSize -= itemSize
popupView.ephemeralHidden = true
totalSize -= itemSize
} else {
if (viewModel.oneToOneChatRoom) {
popupView.groupInfoHidden = true
totalSize -= itemSize
}
// If one participant one device, a click on security badge
// will directly start a call or show the dialog, so don't show this menu
if (viewModel.oneParticipantOneDevice) {
if (!viewModel.encryptedChatRoom) {
popupView.devicesHidden = true
totalSize -= itemSize
}
popupView.ephemeralHidden = true
totalSize -= itemSize
} else {
if (viewModel.oneToOneChatRoom) {
popupView.groupInfoHidden = true
totalSize -= itemSize
}
if (viewModel.ephemeralChatRoom) {
if (chatRoom.currentParams.ephemeralMode == ChatRoomEphemeralMode.AdminManaged) {
if (chatRoom.me?.isAdmin == false) {
Log.w("[Chat Room] Hiding ephemeral menu as mode is admin managed and we aren't admin")
popupView.ephemeralHidden = true
totalSize -= itemSize
// If one participant one device, a click on security badge
// will directly start a call or show the dialog, so don't show this menu
if (viewModel.oneParticipantOneDevice) {
popupView.devicesHidden = true
totalSize -= itemSize
}
if (viewModel.ephemeralChatRoom) {
if (chatRoom.currentParams.ephemeralMode == ChatRoomEphemeralMode.AdminManaged) {
if (chatRoom.me?.isAdmin == false) {
Log.w("[Chat Room] Hiding ephemeral menu as mode is admin managed and we aren't admin")
popupView.ephemeralHidden = true
totalSize -= itemSize
}
}
}
}
@ -911,6 +925,14 @@ class DetailChatRoomFragment : MasterFragment<ChatRoomDetailFragmentBinding, Cha
enterEditionMode()
popupWindow.dismiss()
}
popupView.setMuteListener {
viewModel.muteNotifications(true)
popupWindow.dismiss()
}
popupView.setUnmuteListener {
viewModel.muteNotifications(false)
popupWindow.dismiss()
}
popupWindow.showAsDropDown(binding.menu, 0, 0, Gravity.BOTTOM)
}

View file

@ -78,6 +78,8 @@ class ChatRoomViewModel(val chatRoom: ChatRoom) : ViewModel(), ContactDataInterf
val ephemeralEnabled = MutableLiveData<Boolean>()
val basicChatRoom: Boolean = chatRoom.hasCapability(ChatRoomCapabilities.Basic.toInt())
val oneToOneChatRoom: Boolean = chatRoom.hasCapability(ChatRoomCapabilities.OneToOne.toInt())
val encryptedChatRoom: Boolean = chatRoom.hasCapability(ChatRoomCapabilities.Encrypted.toInt())
@ -99,6 +101,8 @@ class ChatRoomViewModel(val chatRoom: ChatRoom) : ViewModel(), ContactDataInterf
val groupCallAvailable: Boolean
get() = LinphoneUtils.isRemoteConferencingAvailable()
val notificationsMuted = MutableLiveData<Boolean>()
private var addressToCall: Address? = null
private val bounceAnimator: ValueAnimator by lazy {
@ -235,6 +239,8 @@ class ChatRoomViewModel(val chatRoom: ChatRoom) : ViewModel(), ContactDataInterf
callInProgress.value = chatRoom.core.callsNb > 0
updateRemotesComposing()
notificationsMuted.value = areNotificationsMuted()
if (corePreferences.enableAnimations) bounceAnimator.start()
}
@ -250,10 +256,6 @@ class ChatRoomViewModel(val chatRoom: ChatRoom) : ViewModel(), ContactDataInterf
if (corePreferences.enableAnimations) bounceAnimator.end()
}
fun hideMenu(): Boolean {
return chatRoom.hasCapability(ChatRoomCapabilities.Basic.toInt()) || (oneToOneChatRoom && !encryptedChatRoom)
}
fun contactLookup() {
displayName.value = when {
chatRoom.hasCapability(ChatRoomCapabilities.Basic.toInt()) -> LinphoneUtils.getDisplayName(
@ -307,6 +309,17 @@ class ChatRoomViewModel(val chatRoom: ChatRoom) : ViewModel(), ContactDataInterf
formatLastMessage(chatRoom.lastMessageInHistory)
}
fun areNotificationsMuted(): Boolean {
val id = LinphoneUtils.getChatRoomId(chatRoom.localAddress, chatRoom.peerAddress)
return corePreferences.chatRoomMuted(id)
}
fun muteNotifications(mute: Boolean) {
val id = LinphoneUtils.getChatRoomId(chatRoom.localAddress, chatRoom.peerAddress)
corePreferences.muteChatRoom(id, mute)
notificationsMuted.value = mute
}
private fun formatLastMessage(msg: ChatMessage?) {
val builder = SpannableStringBuilder()
if (msg == null) {

View file

@ -84,6 +84,18 @@ class CorePreferences constructor(private val context: Context) {
// logcatLogsOutput = false
}
fun chatRoomMuted(id: String): Boolean {
val sharedPreferences: SharedPreferences = coreContext.context.getSharedPreferences("notifications", Context.MODE_PRIVATE)
return sharedPreferences.getBoolean(id, false)
}
fun muteChatRoom(id: String, mute: Boolean) {
val sharedPreferences: SharedPreferences = coreContext.context.getSharedPreferences("notifications", Context.MODE_PRIVATE)
val editor = sharedPreferences.edit()
editor.putBoolean(id, mute)
editor.apply()
}
/* App settings */
var debugLogs: Boolean

View file

@ -167,7 +167,14 @@ class NotificationsManager(private val context: Context) {
ShortcutsHelper.createShortcutsToChatRooms(context)
}
}
displayIncomingChatNotification(room, message)
val id = LinphoneUtils.getChatRoomId(room.localAddress, room.peerAddress)
val mute = corePreferences.chatRoomMuted(id)
if (mute) {
Log.i("[Notifications Manager] Chat room $id has been muted")
} else {
displayIncomingChatNotification(room, message)
}
}
override fun onChatRoomRead(core: Core, chatRoom: ChatRoom) {

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 750 B

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

View file

@ -11,9 +11,6 @@
<variable
name="titleClickListener"
type="android.view.View.OnClickListener"/>
<variable
name="editClickListener"
type="android.view.View.OnClickListener"/>
<variable
name="menuClickListener"
type="android.view.View.OnClickListener"/>
@ -131,7 +128,6 @@
<ImageView
android:id="@+id/menu"
android:visibility="@{viewModel.hideMenu() || chatSendingViewModel.isReadOnly ? View.GONE : View.VISIBLE}"
android:onClick="@{menuClickListener}"
android:contentDescription="@string/content_description_show_chat_room_menu"
android:layout_width="0dp"
@ -141,17 +137,6 @@
android:padding="15dp"
android:src="@drawable/icon_menu_more"/>
<ImageView
android:onClick="@{editClickListener}"
android:visibility="@{menu.visibility == View.GONE ? View.VISIBLE : View.GONE}"
android:contentDescription="@string/content_description_enter_edition_mode"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="0.2"
android:background="?attr/button_background_drawable"
android:padding="15dp"
android:src="@drawable/delete" />
</LinearLayout>
<androidx.fragment.app.FragmentContainerView

View file

@ -87,7 +87,7 @@
<TextView
android:id="@+id/title"
style="@style/contact_name_list_cell_font"
android:text="@{viewModel.oneToOneChatRoom ? (viewModel.contact.name ?? viewModel.displayName) : viewModel.subject}"
android:text="@{viewModel.oneToOneChatRoom ? (viewModel.contact.name ?? viewModel.displayName) : viewModel.subject, default=`Lorem Ipsum`}"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginRight="30dp"
@ -101,7 +101,6 @@
android:layout_above="@+id/lastMessage"
android:layout_alignParentRight="true"
android:layout_marginRight="5dp"
android:layout_marginLeft="5dp"
android:gravity="center"
android:orientation="horizontal">
@ -131,24 +130,42 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/title"
android:layout_toLeftOf="@id/ephemeral"
android:layout_toLeftOf="@id/icons"
android:ellipsize="end"
android:maxLines="2"
android:text="@{viewModel.lastMessageText}" />
android:text="@{viewModel.lastMessageText, default=`Lorem ipsum dolor sit amet`}" />
<ImageView
android:visibility="@{viewModel.ephemeralEnabled ? View.VISIBLE : View.GONE, default=gone}"
android:id="@+id/ephemeral"
android:contentDescription="@string/content_description_ephemeral_chat_room"
android:layout_width="20dp"
<LinearLayout
android:id="@+id/icons"
android:layout_width="wrap_content"
android:layout_height="20dp"
android:adjustViewBounds="true"
android:orientation="horizontal"
android:layout_alignParentBottom="true"
android:layout_alignParentRight="true"
android:layout_marginRight="5dp"
android:layout_marginLeft="5dp"
android:layout_marginBottom="10dp"
android:src="@drawable/ephemeral_messages"/>
android:gravity="right">
<ImageView
android:visibility="@{viewModel.notificationsMuted ? View.VISIBLE : View.GONE, default=gone}"
android:id="@+id/muted"
android:contentDescription="@string/content_description_muted_chat_room"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:adjustViewBounds="true"
android:layout_marginRight="5dp"
android:src="@drawable/menu_notifications_off"/>
<ImageView
android:visibility="@{viewModel.ephemeralEnabled ? View.VISIBLE : View.GONE, default=gone}"
android:id="@+id/ephemeral"
android:contentDescription="@string/content_description_ephemeral_chat_room"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:adjustViewBounds="true"
android:layout_marginRight="5dp"
android:src="@drawable/ephemeral_messages"/>
</LinearLayout>
</RelativeLayout>

View file

@ -16,6 +16,12 @@
<variable
name="editionModeListener"
type="View.OnClickListener" />
<variable
name="muteListener"
type="View.OnClickListener" />
<variable
name="unmuteListener"
type="View.OnClickListener" />
<variable
name="groupInfoHidden"
type="Boolean" />
@ -25,6 +31,12 @@
<variable
name="ephemeralHidden"
type="Boolean" />
<variable
name="muteHidden"
type="Boolean" />
<variable
name="unmuteHidden"
type="Boolean" />
</data>
<LinearLayout
@ -63,6 +75,26 @@
android:text="@string/chat_message_context_menu_ephemeral_messages"
app:drawableRightCompat="@drawable/chat_room_menu_ephemeral" />
<TextView
android:layout_width="match_parent"
android:layout_height="@dimen/chat_room_popup_item_height"
android:background="@drawable/menu_background"
android:visibility="@{muteHidden ? View.GONE : View.VISIBLE}"
android:onClick="@{muteListener}"
style="@style/popup_item_font"
android:text="@string/chat_message_context_menu_turn_off_notifications"
app:drawableRightCompat="@drawable/menu_notifications_off" />
<TextView
android:layout_width="match_parent"
android:layout_height="@dimen/chat_room_popup_item_height"
android:background="@drawable/menu_background"
android:visibility="@{unmuteHidden ? View.GONE : View.VISIBLE}"
android:onClick="@{unmuteListener}"
style="@style/popup_item_font"
android:text="@string/chat_message_context_menu_turn_on_notifications"
app:drawableRightCompat="@drawable/menu_notifications_on" />
<TextView
android:layout_width="match_parent"
android:layout_height="@dimen/chat_room_popup_item_height"

View file

@ -727,4 +727,7 @@
<string name="conference_start_group_call_dialog_title">Appel de groupe</string>
<string name="conference_start_group_call_dialog_message">Voulez-vous démarrer un appel de groupe ?\nToutes les personnes dans cette conversation vont recevoir un appel.</string>
<string name="conference_start_group_call_dialog_ok_button">Démarrer</string>
<string name="chat_message_context_menu_turn_off_notifications">Désactiver les notifications</string>
<string name="chat_message_context_menu_turn_on_notifications">Activer les notifications</string>
<string name="content_description_muted_chat_room">Les notifications sont désactivées pour cette conversation</string>
</resources>

View file

@ -190,6 +190,8 @@
<string name="chat_room_context_menu_participants_devices">Conversation\'s devices</string>
<string name="chat_message_context_menu_ephemeral_messages">Ephemeral messages</string>
<string name="chat_message_context_menu_delete_messages">Delete messages</string>
<string name="chat_message_context_menu_turn_off_notifications">Disable notifications</string>
<string name="chat_message_context_menu_turn_on_notifications">Enable notifications</string>
<string name="chat_room_ephemeral_fragment_title">Ephemeral messages</string>
<string name="chat_room_ephemeral_messages_desc">This message will be deleted on both ends once it has been read and after the selected timeout.</string>
<string name="chat_room_ephemeral_message_disabled">Disabled</string>
@ -773,6 +775,7 @@
<string name="content_description_contact_is_admin">Contact is an admin in this conversation</string>
<string name="content_description_contact_is_not_admin">Contact isn\'t an admin in this conversation</string>
<string name="content_description_ephemeral_chat_room">Messages are ephemeral in this conversation</string>
<string name="content_description_muted_chat_room">Notifications are disabled for this conversation</string>
<string name="content_description_contact_can_do_encryption">Contact can be invited in encrypted conversations</string>
<string name="content_description_start_chat">Go into conversation</string>
<string name="content_description_start_encrypted_chat">Go into encrypted conversation</string>

View file

@ -11,7 +11,7 @@ buildscript {
} // for com.github.chrisbanes:PhotoView
}
dependencies {
classpath 'com.android.tools.build:gradle:7.2.0'
classpath 'com.android.tools.build:gradle:7.2.1'
classpath 'com.google.gms:google-services:4.3.10'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.6.21"
classpath "org.jlleitschuh.gradle:ktlint-gradle:10.1.0"