Mute/unmute chat room notifications, using 3-dots menu even for basic chat rooms, updated icons
This commit is contained in:
parent
b4fb169e19
commit
cc36a18d2c
16 changed files with 171 additions and 56 deletions
21
CHANGELOG.md
21
CHANGELOG.md
|
@ -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
|
||||
|
|
|
@ -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'
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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) {
|
||||
|
|
BIN
app/src/main/res/drawable-xhdpi/menu_notifications_off.png
Normal file
BIN
app/src/main/res/drawable-xhdpi/menu_notifications_off.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.6 KiB |
BIN
app/src/main/res/drawable-xhdpi/menu_notifications_on.png
Normal file
BIN
app/src/main/res/drawable-xhdpi/menu_notifications_on.png
Normal file
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 |
|
@ -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
|
||||
|
|
|
@ -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>
|
||||
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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>
|
|
@ -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>
|
||||
|
|
|
@ -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"
|
||||
|
|
Loading…
Reference in a new issue