Compare commits

...

2497 commits

Author SHA1 Message Date
Sylvain Berfini
e6f897cb39 Change layout to default one (instead of audion only) when joining a conference using a SIP URI 2026-01-15 15:22:45 +01:00
Sylvain Berfini
97606578a4 Updated gradle to 9.1.0 2026-01-15 11:13:26 +01:00
Sylvain Berfini
48c9b4d7af Updated dependencies 2026-01-15 09:15:03 +01:00
Sylvain Berfini
7d0d1a22ee Fixed text alignment 2026-01-14 19:54:54 +01:00
Sylvain Berfini
d467699388 Factorized code related to copying data into clipboard, added exception catch 2026-01-14 11:53:06 +01:00
Sylvain Berfini
5ad7b5da14 Update chat room shortcuts each time a notification is about to be created + report 'use' when opening a conversation 2026-01-14 09:56:03 +01:00
Sylvain Berfini
50c922b581 Small UI changes to improve look & feel 2026-01-14 09:39:46 +01:00
Sylvain Berfini
b574bf420c Fixed file viewer hover color 2026-01-13 10:20:52 +01:00
Sylvain Berfini
3df3c8d741 Fixed generated avatar size sometimes small 2026-01-12 16:44:25 +01:00
Sylvain Berfini
4039da9c8a Improved meeting layout 2026-01-12 16:19:33 +01:00
Sylvain Berfini
3cec19126d Fixed contacts not updated with LDAP results if latest query fails 2026-01-12 15:23:29 +01:00
Sylvain Berfini
a816a956b8 Added missing hover effects 2026-01-12 15:12:14 +01:00
Sylvain Berfini
cec3639b73 Try to workaround race condition on slow phones that may happen with foreground service notification not displayed in time 2026-01-09 10:53:07 +01:00
Sylvain Berfini
07cae7eb12 When opening delivery status bottom sheet select 'first' not empty tab automatically 2026-01-09 10:30:11 +01:00
Sylvain Berfini
3b561275a4 Close search bar when opening bottom sheet and vice versa 2026-01-08 12:17:24 +01:00
Sylvain Berfini
3f868e02fe Remove highlight from previous match when navigating between search results 2026-01-08 12:06:13 +01:00
Sylvain Berfini
57644a34de Changed mentions color in chat bubble to app's primary 2026-01-08 11:45:46 +01:00
Sylvain Berfini
e306c8c7fc Do not rely on chatMessageSending callback for scenario where message(s) are queued because of delayed subscribe 2026-01-08 09:54:25 +01:00
Sylvain Berfini
dd9190df07 Fixed layout broken by recyclerview id change 2026-01-06 17:29:15 +01:00
Sylvain Berfini
d90861b5f3 Cancel search when making reply/edit/forward action on a message 2026-01-06 16:20:00 +01:00
Sylvain Berfini
9151898a4d Disable search direction after latest matching result found 2026-01-06 16:09:47 +01:00
Sylvain Berfini
fe788caf0e Added label when no participant match user input after typing @ in a group conversation 2026-01-06 12:18:50 +01:00
Sylvain Berfini
7e0353cc91 Prevent participants list from blinking if it hasn't changed, using RecyclerView will also improve performances a bit for conversations with a lot of participants 2026-01-06 10:47:54 +01:00
Sylvain Berfini
a897c127e5 Using onFileTransferTerminated instead of relying on FileTransferDone chat message state 2026-01-05 15:58:18 +01:00
Sylvain Berfini
50aa053c19 Filter participants list using user input after '@' 2026-01-05 15:15:02 +01:00
Sylvain Berfini
b88b6a8093 Fixed hidden reply area when adding file to send area 2026-01-05 14:19:08 +01:00
Sylvain Berfini
24d808b1a7 Hide presence SIP addresses from contact details & editor views 2026-01-05 11:58:35 +01:00
Sylvain Berfini
6f09853424 Fixed attaching file to conversation from third party app using shortcut if matching conversation is already displayed 2026-01-05 10:40:35 +01:00
Sylvain Berfini
a7593e07fc Added a setting to edit native contacts in-app 2026-01-05 09:24:57 +01:00
Sylvain Berfini
844b182df2 Bumped version code 2025-12-18 15:37:12 +01:00
Sylvain Berfini
d299b0b129 Added RC flag to allow disabling add contact feature 2025-12-17 09:43:30 +00:00
Sylvain Berfini
965b159139 Using newly added MWI API to only show the notification bar for the concerned account + call voicemail when clicking on it 2025-12-17 08:09:22 +00:00
Sylvain Berfini
00b8e59ade Updated CHANGELOG with 6.0.21 release info 2025-12-16 16:23:11 +01:00
Sylvain Berfini
be47deeb40 Fixed self avatar not displayed in call views 2025-12-15 14:15:13 +01:00
Sylvain Berfini
e8c67fdd6f Don't use connected bluetooth audio device (if any) for recording a voice message 2025-12-15 11:21:44 +01:00
Sylvain Berfini
ff98c15840 Fixed crash due to empty routes & when setting an empty one in account params 2025-12-15 09:40:15 +01:00
Sylvain Berfini
3711fd749e Bumped AGP to 8.13.2 2025-12-12 10:15:00 +01:00
Sylvain Berfini
dce7095f74 Added workaround for linphone-config:// URIs 2025-12-11 11:09:25 +01:00
Sylvain Berfini
d2b12159af Allow linphone-config URIs in QR codes scanned inside Linphone 2025-12-11 10:25:46 +01:00
Sylvain Berfini
618be9ee7c Fixed contact not updated when company or job title was removed from native contact 2025-12-08 15:25:23 +01:00
Sylvain Berfini
e1abcc6dca Fixed ContactLoader not notifying app of changes when editing a native friend through another app 2025-12-08 15:04:43 +01:00
Sylvain Berfini
2a9ef440b7 Code small improvements 2025-12-05 11:28:41 +01:00
Sylvain Berfini
77b933c5a8 Migrated translations from release/6.0 branch 2025-12-05 11:19:36 +01:00
Sylvain Berfini
6b56165d4f Bumped version code to 6.01.001 and updated CHANGELOG 2025-12-05 10:33:38 +01:00
Sylvain Berfini
61c79a86f7 Added seek to recordings player & media player 2025-12-05 10:04:13 +01:00
Sylvain Berfini
40d195e06b Updated dependencies 2025-12-04 12:03:54 +00:00
Sylvain Berfini
e173e402c2 Added answer/decline keyboard shortcuts to CallActivity 2025-12-04 12:55:52 +01:00
Sylvain Berfini
7817e6603c Force front camera as default device when leaving QR code fragment 2025-12-03 14:19:49 +01:00
Sylvain Berfini
bf4b5a51f5 Added back largeHeap in Manifest XML file 2025-12-02 13:26:18 +01:00
Sylvain Berfini
3ffda24b82 Workaround missing audio focus requests & wrong audio manager mode when TelecomManager APIs aren't available on device 2025-12-02 11:05:11 +01:00
Sylvain Berfini
c99acbb5e1 Added missing update unread chat message count when a message has been retracted 2025-12-01 10:57:08 +01:00
Sylvain Berfini
cc1cc7d929 Fixed crash seen on Crashlytics due to clipboard data text being null 2025-12-01 08:38:09 +00:00
Sylvain Berfini
6bcce4ddbf Fixed call recording wrong indicator in case UPDATE isn't answered 2025-11-25 12:57:09 +01:00
Sylvain Berfini
696a593cbc Prevent replying to retracted message with swipe action 2025-11-24 15:20:48 +01:00
Sylvain Berfini
88e474533e Fixed reply preview when a message has been deleted (locally or remotely) 2025-11-24 14:11:03 +01:00
Sylvain Berfini
8e76c60a38 Added more info to startup listener, also log 3 previous startup reasons 2025-11-24 11:52:17 +01:00
Sylvain Berfini
85aa50d8d8 Fixed in-call media encryption alignment 2025-11-24 10:35:16 +01:00
Sylvain Berfini
c496545023 Added missing try/catch around some startActivity to prevent not found exceptions 2025-11-21 16:36:22 +01:00
Sylvain Berfini
1183a9e1c2 Updated CHANGELOG & version code from release/6.0 branch 2025-11-21 10:01:34 +01:00
Sylvain Berfini
170cd6fccc Cancel voice message recording when starting editing already sent text message 2025-11-20 15:50:56 +01:00
Sylvain Berfini
bc7ac8be64 Fixed issue with recording player fragment layout using lateinit property model 2025-11-18 17:12:44 +01:00
Sylvain Berfini
5ed68e0171 Remove AuthInfo when creating CardDAV entry if synchronization fails 2025-11-18 14:38:22 +01:00
Sylvain Berfini
41e6776b32 Use newly available API to properly remove account and all associated data 2025-11-18 11:38:42 +01:00
Sylvain Berfini
e290a8c4ea Added resources shrink to release build 2025-11-18 10:43:12 +01:00
Sylvain Berfini
93e26f6c10 Fixed missing error toast when starting a group call/meeting and there's an error after adding participants 2025-11-18 10:23:10 +01:00
Sylvain Berfini
3f22a596db Ignore Telecom Manager endpoints availability/requests, using our own preferred endpoint policy (to workaround device disconnect/reconnect not always notified) 2025-11-17 12:22:54 +01:00
Sylvain Berfini
d5c836b8b5 Should prevent crash that may happen after picking ringtone if settings fragment was destroyed while new ringtone was being picked 2025-11-17 10:06:35 +01:00
Sylvain Berfini
9afcb6db15 Updated AGP to 8.13.1 2025-11-17 09:47:56 +01:00
Sylvain Berfini
a6f568497d Bumped dependencies 2025-11-06 09:04:43 +01:00
Sylvain Berfini
209c0df091 Prevent voice message recording when editing an already sent message 2025-11-06 08:50:39 +01:00
Sylvain Berfini
7b0de4185c Prevent message edit to overlap reply and vice-versa 2025-11-05 14:47:13 +01:00
Sylvain Berfini
89458ed826 Bumped firebase BoM 2025-11-03 10:18:34 +01:00
Sylvain Berfini
a3f86fbac0 Fixed toggling favorite flag on contact not adding/removing it from favorites list 2025-10-31 14:37:00 +01:00
Sylvain Berfini
28cee7f539 Hide LDAP verbose mode toggle setting as it has no effect in SDK 2025-10-31 09:44:20 +01:00
Sylvain Berfini
daa2f10f7b Bumped Kotlin version 2025-10-30 12:39:43 +01:00
Sylvain Berfini
e14ea0ac68 Fixed emoji reaction not visible when long pressing an image on a device with a small screen 2025-10-30 09:31:36 +01:00
Sylvain Berfini
c3ad96cd1f Fixed issue in contact layout 2025-10-27 15:24:18 +01:00
Sylvain Berfini
c6a0f25041 Fixed generated avatars color when switching between light/dark modes 2025-10-27 15:17:18 +01:00
Sylvain Berfini
7ab7136a5b Fixed contacts list cell clipping 2025-10-27 14:16:22 +01:00
Sylvain Berfini
3698e1673e Removed delete contact option for native ones, will be re-imported at next restart anyway 2025-10-27 11:20:37 +01:00
Sylvain Berfini
bdd5c8766b Fixed missing bottom margin 2025-10-27 10:30:26 +01:00
Sylvain Berfini
ce2b794936 Fixed conversation avatar not updated when subject changes 2025-10-22 10:02:28 +02:00
Sylvain Berfini
e267f46fd7 Prevent blinking avatars in list in case they didn't change 2025-10-22 09:29:48 +02:00
Sylvain Berfini
ab6911dd11 Fixed adding/editing CardDAV synchronized contact picture 2025-10-21 16:58:38 +02:00
Sylvain Berfini
b0283043ee Save generated avatar as files in cache for faster re-user and lower memory footprint 2025-10-21 15:38:50 +02:00
Sylvain Berfini
0e71a726c1 Fixed infinite LDAP queries loop in case it returns a result that doesn't match the request 2025-10-20 11:29:48 +02:00
Sylvain Berfini
d74ccb523e Prevent LDAP password to be removed after editing existing config 2025-10-20 10:52:06 +02:00
Sylvain Berfini
4dc1b9a903 Disable camera button while in conference with audio_only layout 2025-10-20 10:00:53 +02:00
Sylvain Berfini
45c756cfd6 Updated CHANGELOG & version code from release/6.0 branch 2025-10-16 11:01:31 +02:00
Sylvain Berfini
069997d780 Fixed displayer screen sharing participant name 2025-10-15 17:03:01 +02:00
Sylvain Berfini
e2c9e1196f Force all LDAP fields to be filled, added verbose mode toggle 2025-10-15 12:12:32 +02:00
Sylvain Berfini
e8c642b9c6 Bumped firebase & gms version 2025-10-15 11:02:38 +02:00
Sylvain Berfini
d75c48cd34 Fixed misleading method name 2025-10-15 09:59:13 +02:00
Sylvain Berfini
d9ab840570 Prevent black screen when trying to scan a QR code in assistant right after granting the app the CAMERA permission (on some devices) 2025-10-14 14:23:41 +02:00
Sylvain Berfini
5ee3ba4ea9 Updated warning about conversations that aren't E2E encrypted 2025-10-13 11:55:33 +02:00
Sylvain Berfini
d694789d4b Trying to troubleshoot missing participant video when changing conference layout sometimes 2025-10-13 11:16:31 +02:00
Sylvain Berfini
b71249ea36 Fixed typos in French translation 2025-10-13 09:40:11 +02:00
Sylvain Berfini
7855d4e1db Updated lock open icon & warning color to increase contrast 2025-10-13 09:38:34 +02:00
Sylvain Berfini
7e2527c46c Fixed wrong label for LDAP form field 2025-10-06 16:19:43 +02:00
Sylvain Berfini
d16dbcf0fd Fixed proximity sensor not turned ON when call is answered from notification 2025-10-06 11:08:54 +02:00
Sylvain Berfini
1d28ce1846 Added logs to help troubleshoot contact matching issue 2025-10-06 10:28:23 +02:00
Sylvain Berfini
2ea38abdfe Reworked proxy/outbound proxy settings in account advanced settings 2025-10-02 16:12:00 +02:00
Sylvain Berfini
416cc6ea7f Only display missing permissions in assistant PermissionsFragment 2025-10-02 15:17:05 +02:00
Sylvain Berfini
6dc4790597 Simplified code using newly added API in SDK 2025-10-02 10:51:23 +02:00
Sylvain Berfini
f8556aa46b Hide suggestions SIP address domain if it matches default account SIP identity one + fixed suggestion avatar for phone numbers 2025-10-01 15:12:14 +02:00
Sylvain Berfini
df09bcad76 Hide SIP address field from contact editor when hide SIP addresses flag is set, and fixed issue where dialog with only 1 item would be displayed 2025-10-01 11:41:40 +02:00
Sylvain Berfini
0ca4eba63b Fixed contacts presence subscribe being only enabled for default domain account, added setting to disable presence 2025-10-01 10:01:48 +02:00
Sylvain Berfini
c556d14fb0 Fixed ConcurrentModificationException that could happen during contact edition 2025-09-29 13:59:50 +02:00
Sylvain Berfini
6cb78c8c59 Bumped dependencies 2025-09-26 11:57:52 +02:00
Sylvain Berfini
61517461dd Use account recovery token FlexiAPI endpoint 2025-09-23 20:32:13 +02:00
Sylvain Berfini
1fdc2bcc58 Fixed no suggestion flag not applied for filter text input in some screens (mostly participant pickers) 2025-09-22 16:28:10 +02:00
Sylvain Berfini
8f3415f6fa Reduce limit of attempts to change audio device in Android framework + return correct value if it failed 2025-09-22 14:58:44 +02:00
Sylvain Berfini
ae7a3c5bce Load contents by chunks instead of loading all of them at once 2025-09-17 16:30:41 +02:00
Sylvain Berfini
31e15ddfca Prevent app crash when trying to open a corrupted PDF sent/received by chat 2025-09-17 13:36:15 +02:00
Sylvain Berfini
808dc92cd7 Added PDF file preview in conversation (message bubble + documents list) 2025-09-17 12:18:17 +02:00
Sylvain Berfini
99936e8f75 Removed font padding on main fragments' titles 2025-09-17 09:22:10 +02:00
Sylvain Berfini
2ce07b5e89 Updated CHANGELOG & version code from release/6.0 branch 2025-09-15 14:22:44 +02:00
Sylvain Berfini
9d3ef9e8a5 Fix for empty fragment still opened after device rotation if user clicked on the empty part 2025-09-15 11:26:30 +02:00
Sylvain Berfini
5f17dd8534 Added menu icon in top bar next to current profile avatar + fixed layout icon while in conference 2025-09-15 09:29:22 +02:00
Sylvain Berfini
719b28f0ab Fixed account labelled as Disabled instead of Disconnected if network isn't reachable 2025-09-12 15:09:39 +02:00
Sylvain Berfini
6f1439756e Improved bodyless friendlist presence received processing 2025-09-11 11:14:31 +02:00
Sylvain Berfini
7fdbaf5fd6 Updated dependencies 2025-09-11 09:14:11 +02:00
Sylvain Berfini
4639e054bb Ask CallActivity to finish if no call found when trying to answer/hangup 2025-09-08 10:55:19 +02:00
Sylvain Berfini
504f6e2a2c Fixed missing conference subject when calling it's SIP URI without having the conference info 2025-09-05 15:16:56 +02:00
Sylvain Berfini
1e6f501dee Fixed mute mic / toggle speaker buttons background changing color when pressing the bottom bar empty space 2025-09-05 13:50:33 +02:00
Sylvain Berfini
633aee829a Updated numpad digits from cirlces to squircles to make them bigger 2025-09-05 12:08:39 +02:00
Sylvain Berfini
38ffac31b4 Added missing floating action button for dialpad on call transfer fragment 2025-09-05 11:59:19 +02:00
Sylvain Berfini
6e40e3f75f Updated AGP to 8.13.0 2025-09-03 13:28:21 +02:00
Sylvain Berfini
7d7900e081 Fixed media encryption label show end-to-end encrypted call while in conference instead of point-to-point 2025-09-03 08:42:02 +02:00
Sylvain Berfini
f0e899bb95 Updated CHANGELOG & version code from release/6.0 branch 2025-09-02 16:53:44 +02:00
Sylvain Berfini
db7ca6793b Added swipe/pull to refresh on contacts list when a CardDAV friend list is configured to force the synchronization 2025-09-02 10:39:37 +02:00
Sylvain Berfini
ac521557ce Bumped AGP & Firebase BoM dependencies 2025-09-01 09:56:05 +02:00
Sylvain Berfini
b5babae39a Updated telecom dependency 2025-08-28 18:29:11 +02:00
Sylvain Berfini
ce13d4c7d4 Reworked how meetings are cancelled/deleted from user perspective 2025-08-27 12:12:36 +02:00
Sylvain Berfini
e9cc03891b Remove update available alert for two dependencies 2025-08-26 15:31:22 +02:00
Sylvain Berfini
98bf3daed8 Use newly added SDK API to merge friend list 2025-08-25 16:59:00 +02:00
Sylvain Berfini
a5cee98a57 Updated CHANGELOG & version code from release/6.0 branch 2025-08-25 10:12:56 +02:00
Sylvain Berfini
26e391cbf8 Fixed contacts not displayed in app after being received through bodyless presence 2025-08-21 10:05:21 +02:00
Sylvain Berfini
0add60c628 Remove legacy shortcuts, follow Android guidelines and create chat room shortcuts when a message is sent or received in it, not at the start 2025-08-19 17:18:00 +02:00
Sylvain Berfini
fc90a95e94 Removed trash icon from retract message placeholder 2025-08-19 12:07:56 +02:00
Sylvain Berfini
c153b2d928 Hide reply/forward actions for retracted message 2025-08-19 11:48:27 +02:00
Sylvain Berfini
881e2c217b Reworked GenericAddressPicker related code, updated CorePreferences getters to be @AnyThread instead of @WorkerThread 2025-08-19 11:29:16 +02:00
Sylvain Berfini
77d744d020 Bumped dependencies 2025-08-19 10:46:43 +02:00
Sylvain Berfini
5a16761fdf Fixed methods called from wrong thread, other code cleanup / improvements 2025-08-19 10:38:54 +02:00
Sylvain Berfini
e0ff593f3d Prevent encrypted.pref file from being backed up, at best it won't work at worst it will prevent VFS from being turned on 2025-08-14 12:35:07 +02:00
Sylvain Berfini
71bb569fde Scroll to latest match during chat message search in case user scrolled away 2025-08-14 10:38:48 +02:00
Sylvain Berfini
9096225b45 Bumped dependencies 2025-08-14 09:41:08 +02:00
Sylvain Berfini
ad0037fe4c Do not show delete for everyone/delete locally choice dialog when removing received message 2025-08-14 09:09:55 +02:00
Sylvain Berfini
7eed9c06d3 Reworked some logs in ContactsManager, do not show ContactNumberOrAddressModel if phone number couldn't be turned into an Address 2025-08-11 14:58:17 +02:00
Sylvain Berfini
e2dabf5448 Updated CHANGELOG & version code from release/6.0 branch 2025-08-11 11:51:57 +02:00
Sylvain Berfini
08c72dbb8c Prevent fatal error due to changes in SDK preventing stopping Core from within iterate() loop, fixed leaving assistant after remote provisioning if no account was configured 2025-08-11 10:11:45 +02:00
Sylvain Berfini
f99b51d572 Use newly added Queued chat message state 2025-08-07 18:07:07 +02:00
Sylvain Berfini
8f0f6581b2 Use participant device display name in conference if device name is empty 2025-08-07 16:41:41 +02:00
Sylvain Berfini
fac6e42c22 Added edit/retract message features 2025-08-06 15:38:45 +00:00
Sylvain Berfini
e8d3c8750a Updated CHANGELOG & version code from release/6.0 branch 2025-08-06 17:21:11 +02:00
Sylvain Berfini
dee932da42 Reset forground service wait to stop flag when first call starts, just in case 2025-08-05 12:07:56 +00:00
Sylvain Berfini
2f4fd3da18 Fixed method called from main thread instead of core's one 2025-08-05 10:45:21 +02:00
Sylvain Berfini
22ae4e372f Fixed generated avatar for SIP URIs without username 2025-08-04 17:12:10 +02:00
Sylvain Berfini
95bd14bdd4 Quick workaround to fix missing favorites from address selection fragment 2025-08-04 14:41:45 +02:00
Sylvain Berfini
ffabd02f31 Fixed microphone not recording audio in background if SIP dialog doesn't reach Updating state 2025-08-04 13:51:00 +02:00
Sylvain Berfini
0bb7761db9 Fix outgoing call in full screen 2025-08-01 10:56:50 +02:00
Sylvain Berfini
50418f5dbb Updated AGP to 8.12.0 2025-08-01 09:37:37 +02:00
Sylvain Berfini
e800249445 Updated CHANGELOG & version code from release/6.0 branch 2025-07-31 23:58:11 +02:00
Sylvain Berfini
ec5b6e5707 Updated dependencies 2025-07-31 13:40:33 +02:00
Sylvain Berfini
97c6c0b553 Updated bell and bell_slash icons 2025-07-30 18:46:10 +02:00
Sylvain Berfini
95ce77e0e4 Prevent crash in message notification if person name is empty 2025-07-28 11:49:36 +02:00
Sylvain Berfini
243a6d8cb2 Hide numpad (if visible) from start call fragment when going back using gesture/click 2025-07-28 11:30:16 +02:00
Sylvain Berfini
e98318a23d Fixed numpad padding causing not centered # 2025-07-28 10:38:12 +02:00
Sylvain Berfini
ace0a3f61e Bumped dependencies 2025-07-26 11:57:59 +02:00
Sylvain Berfini
b3ac16052f Prevent exception causing crash if no Activity is available to create document 2025-07-26 11:45:39 +02:00
Sylvain Berfini
865216d717 Remove font padding when chat message only contains emoji 2025-07-26 10:41:40 +02:00
Sylvain Berfini
670eecf0d6 Fixed duplicated week label if 'no meeting today' is first of the week, improved layout a bit 2025-07-25 17:23:08 +02:00
Sylvain Berfini
8dcb18d059 Try to fix video issue when starting video call and video is declined when answered by remote end 2025-07-25 17:23:00 +02:00
Sylvain Berfini
92672bde0a Prevent favourites contacts from missing due to magic search limit 2025-07-25 17:22:51 +02:00
Sylvain Berfini
ada6f35d92 Fixed muted call if Telecom Manager requested a quick mute/unmute 2025-07-25 17:22:42 +02:00
Sylvain Berfini
332828dc7c Fixed french translation 2025-07-23 13:42:01 +02:00
Sylvain Berfini
595ff96d50 Use newly added chat room compose APIs to notify wether text or voice recording is being composed 2025-07-18 15:20:25 +00:00
Sylvain Berfini
6cdcdec373 Updated CHANGELOG & version code from release/6.0 branch 2025-07-18 15:17:51 +02:00
Sylvain Berfini
3098c3e68e Fixed file viewer opening in call activity's if there is one in PiP 2025-07-17 11:55:06 +02:00
Sylvain Berfini
2c9d627794 Updated dependencies 2025-07-17 07:54:16 +00:00
Sylvain Berfini
70df098ee4 Fixed crashes related to lateinit property used before being initialized found on Crashlytics 2025-07-17 07:43:01 +00:00
Sylvain Berfini
c32bac7b07 Added missing operation in progress dialog during account REGISTER in assistant 2025-07-15 16:32:40 +02:00
Sylvain Berfini
3d41a4d221 Added importantForAccessibility=no to all separator views 2025-07-15 14:32:10 +02:00
Sylvain Berfini
dfa87e4088 Improve accessibility by using labelFor item in XML layouts, fixed & uniformized margins in settings 2025-07-15 13:59:04 +02:00
Sylvain Berfini
d6b43c474b Prevent huge and pixelated emojis in picker when device is connected to an external screen 2025-07-15 10:22:43 +02:00
Sylvain Berfini
9d0f2cafc9 Show system notification when account registration is failed 2025-07-13 10:51:57 +02:00
Sylvain Berfini
98cc173d2e Fixed reactions list in bottom sheet not properly updated if opened when changes happen 2025-07-11 16:03:02 +02:00
Sylvain Berfini
4ae046a166 Updated CHANGELOG & version code from release/6.0 branch 2025-07-11 14:39:34 +02:00
Sylvain Berfini
62180140b7 Updated AGP to 8.11.1 2025-07-11 14:28:45 +02:00
Sylvain Berfini
899129d4bc Fixed CardDAV settings when it's read only 2025-07-10 16:22:10 +02:00
Sylvain Berfini
c4618702ab Fixed in-call top bar notification label when going from two calls to one 2025-07-10 13:47:49 +02:00
Sylvain Berfini
8c7c7b40c3 Show user when magic search didn't returned all available results 2025-07-10 11:25:52 +02:00
Sylvain Berfini
856f3e7f94 Fixed issues in French strings 2025-07-09 13:58:17 +02:00
Sylvain Berfini
f7be887984 Fixed wrong english string for password hint in auth requested dialog 2025-07-08 10:32:14 +02:00
Sylvain Berfini
028ece407c Add a way to go to Help page from assistant 2025-07-07 15:19:15 +02:00
Sylvain Berfini
bb81957aab Improved audio device name in advanced settings 2025-07-04 15:16:22 +02:00
Sylvain Berfini
da581c3737 Added enabled toggle for LDAP plugins allowing to disable it without deleting it + reduced max displayed contacts 2025-07-04 11:04:09 +02:00
Sylvain Berfini
461537aa9c Prevent auth dialog when failed to login in assistant and then successfully login another account 2025-07-04 10:15:50 +02:00
Sylvain Berfini
1a54746a80 Added support for HDMI devices 2025-07-03 17:10:32 +02:00
Sylvain Berfini
be24224f4c Bumped dependencies 2025-07-03 09:47:10 +02:00
Sylvain Berfini
f2cdb92858 Reworked LIME algo settings as you can set multiple and order matters 2025-07-02 16:18:08 +02:00
Sylvain Berfini
aa0255bcfd Use read-only information from FriendList if any to hide edit/delete/mark as favortie buttons in contact details & contacts list long press menu 2025-07-01 09:24:58 +02:00
Sylvain Berfini
ff425089c7 Reworked UI for incoming call screen when screen is locked 2025-07-01 08:49:32 +02:00
Sylvain Berfini
b4c2a52bf7 Added LIME algorithm dropdown in account advanced settings if developer mode is enabled 2025-06-30 17:04:26 +02:00
Sylvain Berfini
f397456879 Updated CHANGELOG & version code to match 6.0.10 release 2025-06-27 10:10:37 +02:00
Sylvain Berfini
5337ab6413 Make sure an account is set in CallParams when initiating a call 2025-06-26 13:06:36 +02:00
Sylvain Berfini
998f969c0f Increased single media max height for small screens in portrait orientation or large screens 2025-06-26 12:04:12 +02:00
Sylvain Berfini
58410ee112 Fixed mentions in chat message if there are more than one 2025-06-26 09:53:59 +02:00
Sylvain Berfini
6c97ee9176 Bumped dependencies 2025-06-25 16:57:52 +02:00
Sylvain Berfini
187946bf34 Fixed chat bubble when reply original message is missing (reply UI is hidden) 2025-06-24 16:45:57 +02:00
Sylvain Berfini
3c40bf3d6f Show event at top of conversation in case of unsecure conversation (like we do for e2e encrypted ones) + icon under conversation title 2025-06-23 11:42:07 +02:00
Sylvain Berfini
b7a9f4ba8e Fixed log + missing elevation for nav bar in landscape 2025-06-23 11:22:00 +02:00
Sylvain Berfini
4a1c5304b1 Fixed issue with encryption label being stuck in 'waiting for encryption' 2025-06-23 09:34:28 +02:00
Sylvain Berfini
67e3c51a84 Code cleanup, bumped dependencies & gradle 2025-06-20 13:54:47 +02:00
Sylvain Berfini
4d8ab32da7 Using a style for top bar icons to prevent duplicating code 2025-06-20 11:30:46 +02:00
Sylvain Berfini
62ff36e7a7 Reworked UI for dialogs buttons 2025-06-19 15:34:15 +02:00
Sylvain Berfini
6f80409086 Trying to prevent BT not being used automatically when it connects during a call + improved AudioUtils logs 2025-06-19 13:24:11 +02:00
Sylvain Berfini
fd3f746e3d Prevent race condition crash with VuMeter when call is ending 2025-06-19 12:03:24 +02:00
Sylvain Berfini
42fbbc51fd Improved NotificationsManager code, added call notification PendingIntent's ActivityOptions bundle, use PendingIntent in CoreContext to start CallActivity 2025-06-19 09:53:36 +02:00
Sylvain Berfini
60c74ee5b2 Fixed logs related to developer settings 2025-06-17 20:27:46 +02:00
Sylvain Berfini
79212a8757 Improved process to enable developer settings 2025-06-17 20:19:21 +02:00
Sylvain Berfini
1307ec5471 Added developer setting to change push notification compatible domains list 2025-06-17 17:38:15 +02:00
Sylvain Berfini
c62f549521 Added vu meter developer setting 2025-06-17 16:50:43 +02:00
Sylvain Berfini
9ba2684f31 Using contextClickListener to open bottom sheets/menus when mouse right button is clicked 2025-06-17 14:23:47 +02:00
Sylvain Berfini
4b631a19ef Fixed extra action visible under clear search field icon + fixed search field hint text color 2025-06-17 11:11:48 +02:00
Sylvain Berfini
496279d724 Added pressed/hover effect to icons that can be clicked 2025-06-17 10:40:34 +02:00
Sylvain Berfini
c25ed404dc Added missing hint to every top bar filter/search field, changed empty fragment background color to match the one on desktop app, removed useless title in call history details 2025-06-16 17:12:49 +02:00
Sylvain Berfini
a81973e4cf Using style to factorize popup menus attributes, added hover effect (same as pressed) & improved some layouts 2025-06-16 15:10:19 +02:00
Sylvain Berfini
654e790a6d Also handle text waiting to be shared in newly added top bar 2025-06-16 13:39:53 +02:00
Sylvain Berfini
6602c7692b Reworked MainActivity's top bar, split alerts & in-call notifications, added file sharing dedicated one 2025-06-16 08:48:56 +00:00
Sylvain Berfini
cc57244b56 Hide SIP address/phone number picker dialog if contact has exactly one SIP address matching the default domain and currently default account domain 2025-06-16 09:36:12 +02:00
Sylvain Berfini
19df3b07dc Increase text size when chat bubble only contains emoji character(s) 2025-06-13 16:22:44 +02:00
Sylvain Berfini
4ef9a2bdf3 Increase number of columns in conversation's media list when screen is large 2025-06-13 15:30:25 +02:00
Sylvain Berfini
61be1d21d5 Improved UI on large tablets 2025-06-13 15:00:55 +02:00
Sylvain Berfini
8148354901 Added enable/disable speaker to active call notification 2025-06-13 11:25:28 +02:00
Sylvain Berfini
ae39d79420 Various improvements 2025-06-13 09:20:01 +02:00
Sylvain Berfini
2bd0de4af1 Fixed group conversation creation if LIME server URL not set 2025-06-10 12:07:45 +02:00
Sylvain Berfini
cb27b35984 Updated changelog & version code/name from release/6.0 branch 2025-06-06 15:47:32 +02:00
Sylvain Berfini
316bc6698a Added user guide link in Help section, factorized code for URL opening, added missing ScrollView to help & debug layouts 2025-06-06 11:53:22 +02:00
Sylvain Berfini
fea1dbe5ca Display last message timestamp in conversations list instead of chat room last updated timestamp 2025-06-05 10:28:58 +02:00
Sylvain Berfini
85679dcc43 Added vu meter for playback volume + setting to enable vu meters (disabled by default) 2025-06-05 09:51:21 +02:00
Sylvain Berfini
32060f6830 Bumped dependencies 2025-06-05 09:39:23 +02:00
Sylvain Berfini
dfdc26a575 Prevent crash for call notification due to empty person name, using all identification fields from vCard (first & last names, organization, job title) 2025-06-02 14:08:56 +02:00
Sylvain Berfini
9e1d358f4e Check if outgoing early-media call is really doing video instead of assuming it is 2025-06-02 11:01:30 +02:00
Sylvain Berfini
90922568b5 Prevent port from being set in third party account SIP identity + update existing accounts to remove port from identity 2025-06-02 10:54:32 +02:00
Sylvain Berfini
d212b7b06e Added setting to hide contacts without SIP address nor phone number 2025-05-30 10:09:56 +02:00
Sylvain Berfini
4cb83980ba Bumped AGP 2025-05-30 09:42:33 +02:00
Sylvain Berfini
def52f69ad Only refresh conversation list cell when a message is deleted, prevents blinking 2025-05-29 11:40:42 +02:00
Sylvain Berfini
17ce34aba7 Updated changelog & version code/name from release/6.0 branch 2025-05-23 15:04:55 +02:00
Sylvain Berfini
1833b1985d Fixed sent files size missing 2025-05-22 14:45:31 +02:00
Sylvain Berfini
5256ee79c6 Use gray background for file preview in attachment area to increase contrast, improved remove file from attachments icon size and position 2025-05-22 10:49:13 +02:00
Sylvain Berfini
27e59a5f8b Using utils method to check whether call has video enabled or not 2025-05-21 13:41:24 +00:00
Sylvain Berfini
cea2d49778 Trying to workaround hearing aids issue in Telecom Manager 2025-05-21 15:19:42 +02:00
Sylvain Berfini
c0f67d01fe Prevent crash in MediaViewerFragment if media player wasn't initialized 2025-05-21 10:11:18 +02:00
Sylvain Berfini
b6279b03c0 Make sure that files grid in chat bubble is using at most 3 columns 2025-05-20 16:33:22 +02:00
Sylvain Berfini
21398c7b37 Added sliding button to answer/decline incoming call if device screen is locked 2025-05-20 15:53:21 +02:00
Sylvain Berfini
4cb7ea1965 Showing files in square area like media when more than one in a single chat message 2025-05-20 10:45:27 +02:00
Sylvain Berfini
7aae03f1f9 Fixed missing margin in media grid for audio files 2025-05-19 13:37:45 +02:00
Sylvain Berfini
25d13f44c7 Prevent 1-1 events for conference joined/left + temporary read only state 2025-05-16 15:51:28 +02:00
Sylvain Berfini
81d0da4241 Updated coil dependency 2025-05-16 12:14:37 +02:00
Sylvain Berfini
1556abc79e Fixed logs sharing server URL setting 2025-05-16 11:19:45 +02:00
Sylvain Berfini
28b6bd7e90 Updated changelog & version code/name from release/6.0 branch 2025-05-16 11:13:58 +02:00
Sylvain Berfini
1c3173b871 Moved call related advanced parameters into dedicated fragment 2025-05-16 09:51:32 +02:00
Sylvain Berfini
17588de5a9 Do not delete chat rooms when removing account, will cause leaving groups in case of multi device 2025-05-16 09:40:08 +02:00
Sylvain Berfini
502c6413ee Reworked bottom nav bar (in portrait) 2025-05-16 07:37:06 +00:00
Sylvain Berfini
02cbb45de9 Trying to prevent another race condition in notifications manager leading to foreground service not being started before being stopped 2025-05-16 09:11:38 +02:00
Sylvain Berfini
f5852a7b3e Prevent bottom nav bar titles from being cropped when font size is increased 2025-05-14 08:39:48 +02:00
Sylvain Berfini
cfec621787 Fixed group chat event icons 2025-05-13 16:35:51 +02:00
Sylvain Berfini
6847227f1a Reworked click on SIP URI in chat message to prevent long press on it from starting the call 2025-05-13 14:02:54 +02:00
Sylvain Berfini
f1fdb186ec Reworked unread count indicators 2025-05-13 13:48:15 +02:00
Sylvain Berfini
d822cbc827 Make sure after a remote provisioning a default account has been set 2025-05-13 10:15:42 +02:00
Sylvain Berfini
627f881364 Make sure speaker audio device is used if available when incoming early media call is ringing 2025-05-13 10:09:50 +02:00
Sylvain Berfini
244061c0b1 Fixed black thumbnails when joining conference without bundle mode 2025-05-12 16:23:51 +02:00
Sylvain Berfini
dc2b94ca4d Fixed broken link in README 2025-05-12 13:23:05 +02:00
Sylvain Berfini
7c78b021db Removed some debug logs, improved findContactByAddress performances a bit 2025-05-12 10:29:05 +02:00
Sylvain Berfini
d113797dfb Added auto answer with video in both directions advanced call setting 2025-05-12 09:56:38 +02:00
Sylvain Berfini
926b8d4dc1 Updated dependencies 2025-05-09 11:09:20 +02:00
Sylvain Berfini
85e24e25bf Added missing toast events observer 2025-05-07 16:12:34 +02:00
Sylvain Berfini
1c1729f3f0 Fixed meeting list yesterday item still displayed as today if list isn't reloaded in two days 2025-05-07 11:13:51 +02:00
Sylvain Berfini
bcce9a9ba1 Updated AGP to 8.10 2025-05-07 11:08:00 +02:00
Sylvain Berfini
a496e2bf56 Forgot to disable IMDN bottom sheet for incoming messages in groups 2025-05-06 14:33:19 +02:00
Sylvain Berfini
73237ee335 Added toast for meeting update error + fixed other toasts 2025-05-06 12:53:28 +02:00
Sylvain Berfini
b293bf7f2f Added back Weblate section to README 2025-05-05 15:42:36 +02:00
Sylvain Berfini
4689b7c7da Fixed app reloading lists too many times at startup when looking for friends in remote contact directories such as LDAP/CardDAV 2025-05-05 14:50:22 +02:00
Sylvain Berfini
e38040428b Improved empty lists (contacts & conversations) labels + added button to let user know it can change account 2025-05-05 13:52:28 +02:00
Sylvain Berfini
b740409642 Update conversations list after clearing conversation history 2025-05-05 12:01:23 +02:00
Sylvain Berfini
99a5ed23f6 Updated CHANGELOG & version code from release/6.0 branch 2025-05-02 14:37:23 +02:00
Sylvain Berfini
c9a3a01733 Improved empty SIP contacts list 2025-04-30 15:40:49 +02:00
Sylvain Berfini
966f713f19 Increased margin for clean/share logs button in troubleshooting fragment 2025-04-29 17:15:04 +02:00
Sylvain Berfini
344afdfcfa Moved print logs in logcat & file sharing URL settings to developper section, added logs upload file sharing server URL setting, added setting to disable crashlytics logs collection 2025-04-29 16:54:20 +02:00
Sylvain Berfini
2634945b8d Improved chat room lookup while in conference 2025-04-29 14:18:52 +02:00
Sylvain Berfini
2713c82ca3 Added content description french translation 2025-04-29 09:02:36 +00:00
Sylvain Berfini
1a813ee11e Prevent crash due to uncaught exception 2025-04-29 09:33:38 +02:00
Sylvain Berfini
e5cec2d45c Another attempt at fixing crashes related to in-call service never truly started as foreground before being stopped 2025-04-28 16:06:09 +02:00
Sylvain Berfini
6e9c6d1b33 Fixed group chat events icon 2025-04-28 11:26:35 +02:00
Sylvain Berfini
056abd629f Fixed newly created contact not appearing in contacts list 2025-04-24 10:10:15 +02:00
Sylvain Berfini
6c86af747b Improved VFS confirmation dialog message 2025-04-24 10:04:10 +02:00
Sylvain Berfini
f7790fbed7 Bumped AGP version & splashscreen dependency 2025-04-24 09:47:10 +02:00
Sylvain Berfini
90524da610 Fixed chat room lookup while in call 2025-04-23 10:18:25 +02:00
Sylvain Berfini
616b7bb70f Prevent crash & show error toast when trying to open a password protected PDF 2025-04-23 09:06:51 +02:00
Sylvain Berfini
985a304df9 Updated CHANGELOG & version code from release/6.0 branch 2025-04-18 11:06:19 +02:00
Sylvain Berfini
cd35f213c1 Fixed crash due to missing foreground service if OS denies call notification until foreground Service was started + fixed crash if call is ended before CoreInCallService was started and foreground service notification sent 2025-04-18 09:53:57 +02:00
Sylvain Berfini
dcbc837106 Apply workaround when making a call to a SIP URI having a phone number as username & IP as domain 2025-04-17 14:30:23 +02:00
Sylvain Berfini
5ef7eab0c5 Added microphone volume vu meter 2025-04-17 13:07:15 +02:00
Sylvain Berfini
c64bd5bc1c Fixed numpad dial button while transfering a call 2025-04-17 09:45:53 +02:00
Sylvain Berfini
afa041baf6 Hide account creation form when device doesn't support push notifications 2025-04-17 09:45:02 +02:00
Sylvain Berfini
94b6db6a08 Fixed build with latest SDK 2025-04-17 09:40:52 +02:00
Sylvain Berfini
6ba8760be7 Improved called account display 2025-04-15 11:41:21 +02:00
Sylvain Berfini
b1b1ab0d8a Updated CHANGELOG & version code from release/6.0 branch 2025-04-11 10:28:59 +02:00
Sylvain Berfini
518ecc1823 Added a list of domain for which to show push notification settings 2025-04-11 09:38:55 +02:00
Sylvain Berfini
e2dfd95857 Prevent crash in HelpViewModel if app is built without Firebase 2025-04-10 14:12:39 +02:00
Sylvain Berfini
51d725c757 Quick code cleanup 2025-04-10 13:24:10 +02:00
Sylvain Berfini
af3b1fa418 Bumped dependencies 2025-04-10 10:04:30 +02:00
Sylvain Berfini
bc9a6581b1 Added logs to call transfer (blind & attended) 2025-04-10 09:45:03 +02:00
Sylvain Berfini
26df085df3 Hide push notification setting in third party SIP accounts parameters, they won't work anyway + disable push for existing third party SIP accounts when migrating to 6.0.4 2025-04-09 10:28:23 +02:00
Sylvain Berfini
c08157b659 Removed code no longer needed, done by SDK now + prevent onContactsLoaded() callback to be triggered too many times when fetching multiple addresses from remote contacts directories 2025-04-07 09:54:14 +02:00
Sylvain Berfini
910527ef1b Updated CHANGELOG & version code from release/6.0 branch 2025-04-04 12:59:34 +02:00
Sylvain Berfini
8577571e67 Show operation in progress during contact search 2025-04-04 10:07:32 +02:00
Sylvain Berfini
dbca62bea9 Added hidden developer settings 2025-04-03 17:31:14 +02:00
Sylvain Berfini
5e9be7d10b Show alert when default account is disabled 2025-04-03 14:52:16 +02:00
Sylvain Berfini
836deaae99 Fixed no default account issue when removing currently default one 2025-04-03 13:06:49 +02:00
Sylvain Berfini
a2680028ce Keep attach file icon when keyboard is opened in chat instead of emoji picker 2025-04-03 11:36:50 +02:00
Sylvain Berfini
c8ff7262d4 Follow contacts list filter for every contact/address picker 2025-04-03 10:47:54 +02:00
Sylvain Berfini
06d8e903fc Revert "Trying to prevent bottom bar from disappearing sometimes", trying better fix instead
This reverts commit 317a7c4417.
2025-04-03 09:53:53 +02:00
Sylvain Berfini
a5872ef8de Set default values for notification channels, do not rely only on importance level 2025-04-02 15:15:09 +02:00
Sylvain Berfini
9255830fe2 Show copy SIP URI icon & do it on click in call history like in conversation details 2025-04-02 14:20:13 +02:00
Sylvain Berfini
80eaf08fbf Refresh lists content when going back from background after at least 1 hour (when keep alive service is enabled) 2025-04-01 13:25:16 +02:00
Sylvain Berfini
903aaad6fe Do not store friends map in ContactsLoader, might cause concurrent modification 2025-03-31 16:20:37 +02:00
Sylvain Berfini
bdb2615300 Targetting Android 16 Baklava (API level 36) 2025-03-31 14:15:26 +02:00
Sylvain Berfini
bab2acb75c Prevent meetings list display issue if source isn't sorted 2025-03-31 13:31:19 +02:00
Sylvain Berfini
bd52960749 Fixed behavior when video is disabled in settings, should not show incoming video calls as video nor route audio to speaker automatically 2025-03-31 12:07:06 +02:00
Sylvain Berfini
23810e41e5 Forgot to change some of POST_NOTIFICATIONS checks 2025-03-31 09:26:22 +02:00
Sylvain Berfini
3f3a229844 Updated Github issue template with 6.0 way of sharing logs 2025-03-28 20:05:43 +01:00
Sylvain Berfini
0eb659b633 Updated CHANGELOG from release/6.0 branch 2025-03-28 10:25:14 +01:00
Sylvain Berfini
c35a44b1a0 Fixed migration scenario where logs upload sharing server url might not be set 2025-03-28 10:22:24 +01:00
Sylvain Berfini
1cccf7d26b Apply same call history workaround for missed calls count 2025-03-28 10:22:18 +01:00
Sylvain Berfini
0fab732e89 Fixed LDAP/remote CardDAV results not always displayed when making a search in contacts list 2025-03-27 16:17:49 +00:00
Sylvain Berfini
317a7c4417 Trying to prevent bottom bar from disappearing sometimes 2025-03-27 15:28:57 +01:00
Sylvain Berfini
689665c475 Show floating action button to open numpad in outgoing early media call, prevent display name & SIP address being displayed twice if early media is audio only 2025-03-27 11:32:11 +01:00
Sylvain Berfini
18e15b60a4 Fixed toggle setting color when disabled & off, disable early media ringing toggle if early media ringing is disabled 2025-03-27 10:14:25 +01:00
Sylvain Berfini
7bead679ad Delete all data related to account being removed 2025-03-27 09:40:07 +01:00
Sylvain Berfini
f0ad67fb29 Fixed disabled handle color while outgoing call is ringing 2025-03-27 09:10:22 +01:00
Sylvain Berfini
2621eb306e Added content message to keep app alive foreground service notification 2025-03-26 16:21:55 +01:00
Sylvain Berfini
90bf20e50e Prevent meeting icons from being briefly visible when selected account has no videoconference factory URI set in it's params 2025-03-26 09:48:03 +01:00
Sylvain Berfini
1f45ba8bd0 Added back ring during early media setting 2025-03-26 09:19:37 +01:00
Sylvain Berfini
c528f0cdb8 Bumped dependencies 2025-03-25 17:14:06 +01:00
Sylvain Berfini
a0108776dd Keep newly created account disabled until SMS code validation is done 2025-03-25 16:04:36 +00:00
Peio Rigaux
a503ef06ee Now use docker to deploy. Allows multiple deploy at the same time 2025-03-25 15:55:13 +00:00
Sylvain Berfini
fbc19c7053 Fixes regarding contacts list filter when switching account 2025-03-25 16:45:56 +01:00
Sylvain Berfini
9c8c5f309e Added hidden setting to allow hiding SIP URIs, show device name instead of SIP full SIP URI when doing trust call from contact details 2025-03-25 16:28:00 +01:00
Sylvain Berfini
b40fbcad77 Log TelecomManager CallControl failed operations 2025-03-25 13:27:24 +00:00
Sylvain Berfini
8dda38a925 Parse friends in a coroutine scope, no need to do it on the Core's thread 2025-03-25 13:41:47 +01:00
Sylvain Berfini
d150027c24 Delay heavy tasks to prevent ServiceDidNotStartInTimeException (for example) 2025-03-25 13:16:18 +01:00
Sylvain Berfini
7018cd3442 Fixed crash when default device URI is null 2025-03-24 23:43:46 +01:00
Sylvain Berfini
d6494cd27c Ask for full screen intent if not granted 2025-03-24 17:33:52 +01:00
Sylvain Berfini
10f2d7cd78 Prevent today indicator in meetings list from blinking upon refresh 2025-03-24 14:03:43 +01:00
Sylvain Berfini
6767bc09f9 Fixed displayed SIP URI in call history details in case we find a matching contact that has a different SIP URI in addition to the one used for the call 2025-03-24 13:36:22 +01:00
Sylvain Berfini
b22ab7024e Fixed contact lookup if phone number starts by 00 instead of + 2025-03-24 13:34:10 +01:00
Sylvain Berfini
c6fa645f94 Fixed invisible conference notification icon 2025-03-24 11:39:03 +01:00
Sylvain Berfini
fb3feb0bc3 This should prevent crashes on Androids < 13 due to broken POST_NOTIFICATIONS permission check 2025-03-24 11:33:00 +01:00
Sylvain Berfini
77f61c1cfa Updated version code to match the one on release/6.0 branch 2025-03-24 10:17:31 +01:00
Sylvain Berfini
9ce803667b Do not send meeting message invitation when chat is disabled 2025-03-24 09:53:11 +01:00
Sylvain Berfini
50bd8f67d5 Check if lateinit conference was initialized 2025-03-24 09:44:47 +01:00
Sylvain Berfini
faac4111d9 Updated CHANGELOG from release/6.0 branch 2025-03-24 08:58:28 +01:00
Sylvain Berfini
6121cb41bf Fixed remove all call logs when workaround is active 2025-03-24 08:58:15 +01:00
Sylvain Berfini
2aed404167 Have automaticallyShowDialpad setting also work on new call/transfer call screens while already in call 2025-03-24 08:58:05 +01:00
Sylvain Berfini
2f9eb2f0ab Improved message when WiFi only mode is enabled & active network isn't Wifi nor Ethernet 2025-03-21 11:13:01 +01:00
Sylvain Berfini
1255d626af Fixed recordings order, now most recent on the top 2025-03-21 09:03:26 +01:00
Sylvain Berfini
a83f9d4424 Moved call related advanced settings to dedicated sub-section, added back auto answer 2025-03-21 08:54:17 +01:00
Sylvain Berfini
fecf067b50 Fixed wrong setting disabled when VFS is enabled 2025-03-21 08:41:24 +01:00
Sylvain Berfini
b194272f91 Use newly added chatRoom.getAccount() to dynamically filter conversations 2025-03-20 14:59:32 +01:00
Sylvain Berfini
cad90752db Fixed call logs details if it's not attached to any account 2025-03-20 14:55:49 +01:00
Sylvain Berfini
2abad0ab9a Fetch call history from Core instead of Account if only one of them 2025-03-20 13:00:10 +01:00
Sylvain Berfini
a0d74c8036 Added back apply prefix to phone numbers for calls & chat setting 2025-03-20 12:29:27 +01:00
Sylvain Berfini
2eb376fd2d Prevent schedule meeting button being pressed multiple times 2025-03-19 13:58:12 +01:00
Sylvain Berfini
1942ee8f85 Few tweaks trying to prevent jni global ref table overflow 2025-03-18 16:31:38 +00:00
Sylvain Berfini
488a0fd98c Added advanced setting letting user choose whether to record in MKV or SMFF 2025-03-18 16:46:12 +01:00
Sylvain Berfini
08412ef99a Prevent some crashes seen on Play Store console 2025-03-18 12:51:25 +01:00
Sylvain Berfini
e16e767d5a Fixed wrong deleted notification channel ID... 2025-03-18 12:34:22 +01:00
Sylvain Berfini
886be9e038 Fixed hearing aid icon not showing in bottom actions when selected + updated earpiece icon in device list to match 2025-03-18 11:58:02 +01:00
Sylvain Berfini
2a5b5d368c Revert using notification channel to play incoming calls ringtone 2025-03-18 10:50:53 +01:00
Sylvain Berfini
be5428aa08 Add generic exception handle for starting action_view activity for URL 2025-03-18 10:13:17 +01:00
Sylvain Berfini
d6c6de2b5e Wait for foreground service to be started before being stopped to try preventing ForegroundServiceDidNotStartInTimeException/RemoteServiceException due to Context.startForegroundService() did not then call Service.startForeground() 2025-03-17 17:37:43 +01:00
Sylvain Berfini
915a847083 Prevent crash due to service being started as foreground if post_notifications permission isn't granted 2025-03-17 17:37:43 +01:00
Sylvain Berfini
0d8397b914 Should fix hearing aids issue 2025-03-17 16:20:34 +00:00
Sylvain Berfini
b5a1e21f40 Should fix quit button visibility in drawer menu 2025-03-17 16:10:12 +01:00
Sylvain Berfini
9837a834d4 Added back "replace + by 00 when formatting phone numbers" account setting 2025-03-17 14:38:01 +01:00
Sylvain Berfini
8a4956e7c1 Hidden save/export buttons for call recordings until export feature will be added to SDK 2025-03-17 14:21:54 +01:00
Sylvain Berfini
052d7cc522 Added UI setting to have dialpad automatically opened when starting new call 2025-03-17 14:19:14 +01:00
Sylvain Berfini
6c6fb9eff3 Prevent call transfer if state is Ended, Error or Released 2025-03-17 13:10:38 +01:00
Sylvain Berfini
b23f52adec Prevent system call notification to be stuck if call was ended in Linphone SDK before being added to TelecomManager's 2025-03-17 11:58:02 +01:00
Sylvain Berfini
8769a47ed0 Adding back auto start setting 2025-03-17 11:47:29 +01:00
Sylvain Berfini
ebb7201701 Prevent crash if contacts listener triggers before chatRoom property is initialized 2025-03-17 09:23:44 +00:00
Sylvain Berfini
6e83b794b3 Prevent crash if not on contact fragment before navigating to editor 2025-03-17 09:14:33 +00:00
Sylvain Berfini
3045378eb0 Prevent crash if fails to go to outside activity because it doesn't exists or it isn't found 2025-03-17 09:14:33 +00:00
Sylvain Berfini
dc4619a7d7 Prevent use of recording ViewModel property not initialized yet 2025-03-17 09:06:15 +00:00
Sylvain Berfini
87b6c2deef Prevent crash clinking on link if no browser is installed on device 2025-03-17 08:44:33 +00:00
Sylvain Berfini
614ac7f9cf Prevent crash if DMTF setting doesn't exists (Samsung A51) 2025-03-16 20:34:29 +01:00
Sylvain Berfini
71e1734ca0 Fixed crash due to currentCall not being initialized 2025-03-13 15:28:11 +01:00
Sylvain Berfini
71b1cf8e7a Make sure Qr Code fragment doesn't use Static Picture camera device 2025-03-13 12:17:19 +01:00
Sylvain Berfini
dee684b364 Added setting to choose whether to sort contacts by first or last name 2025-03-13 11:54:46 +01:00
Sylvain Berfini
0b6805a73c Fixed color selector used when not needed, may cause crash on old devices 2025-03-13 10:10:47 +01:00
Sylvain Berfini
11795cded8 Prevent SecurityException when accessing bluetooth_name on some devices 2025-03-13 09:58:00 +01:00
Sylvain Berfini
cc5bfcf14d Use Account's onConferenceInformationUpdated callback to refresh meetings list 2025-03-13 09:45:19 +01:00
Sylvain Berfini
b3ab9601b2 Prevent user from connecting the same account multiple times 2025-03-12 13:32:11 +01:00
Sylvain Berfini
0e6d91a467 Fixed selecting participant in group conversation when typing '@' 2025-03-12 13:05:01 +01:00
Sylvain Berfini
fbf68db2dd Updated linphone version to use 5.5.0-alpha 2025-03-11 16:28:51 +01:00
Sylvain Berfini
0bf50f1495 Fixed wrong constraint layout reference 2025-03-11 16:23:32 +01:00
Sylvain Berfini
8363d41441 Merge branch 'release/6.0' 2025-03-11 16:01:38 +01:00
Sylvain Berfini
c1e20af56d 6.0.0 release, updated CHANGELOG 2025-03-11 13:28:07 +00:00
Sylvain Berfini
5d14d8bb7b Fixed SSO login from QRcode 2025-03-11 14:21:29 +01:00
Sylvain Berfini
a99067f701 Updated SSO ViewModel to allow for client ID to be changed through remote provisioning or to use client_id from URL query parameters 2025-03-11 10:56:18 +01:00
Sylvain Berfini
2ceebdcdda Use same max width for spinners than for text fields in settings, increased that value a bit for tablets 2025-03-11 10:22:24 +01:00
Sylvain Berfini
dca10c9b2a Keep SRTP as default media encryption for now, update active call notification channel importance from default to low 2025-03-11 10:12:39 +01:00
Sylvain Berfini
82e341ea8c Moved RLS URI out of linphonerc_factory 2025-03-10 17:45:03 +01:00
Sylvain Berfini
508f1154f5 Fixed broken scroll listener in conversation after the fragment is paused/resumed 2025-03-10 16:19:53 +01:00
Sylvain Berfini
07cb09128e Show participant SIP URI in reactions / delivery status / conversation participants lists when clicking on name 2025-03-10 16:06:46 +01:00
Sylvain Berfini
d6d705a975 Code cleanup 2025-03-10 11:47:30 +01:00
Sylvain Berfini
c709c720e4 Prevent being stuck while media list is processed 2025-03-10 10:44:05 +01:00
Sylvain Berfini
96a07fa8c6 Replaced Uri.parse() by .toUri() string extension 2025-03-05 15:17:29 +01:00
Sylvain Berfini
141d7b08a6 Improved answered call log 2025-03-05 15:08:09 +01:00
Sylvain Berfini
3c21044cf0 Renamed Android Auto related classes 2025-03-05 13:53:10 +01:00
Sylvain Berfini
e0e7032827 Updated AGP to 8.9.0 and gradle to 8.11.1 2025-03-05 10:46:29 +01:00
Sylvain Berfini
4cca59a39f Use newly added APIs to get real information about a content being related to an ephemeral chat message or not, and use that information to hide save/export buttons & prevent screenshots 2025-03-05 00:42:11 +00:00
Sylvain Berfini
4e852601fc Use new core.createChatRoom() that replaces the older version (API change was introduced in SDK release/5.4 branch commit ID dd4b83553fc72ca5449adb6ce72d9882f90ae320) 2025-03-04 16:48:25 +01:00
Sylvain Berfini
30364c48b0 Clear auth info password requested dialog when account is removed 2025-03-04 13:15:23 +00:00
Sylvain Berfini
354f39d76d Fixed media list fragment when VFS is enabled, the first time it was opened it wasn't possible to scroll to other files 2025-03-04 09:45:43 +01:00
Sylvain Berfini
64a2c5f455 Show green toast when VFS is successfully enabled 2025-03-04 09:01:27 +01:00
Sylvain Berfini
9ffe3b4d7f Hide incoming messages delivery status in groups if IMDN threshold is set to 1 2025-03-03 14:53:40 +01:00
Sylvain Berfini
fb323a4606 Do not play sound for currently displayed conversation if message is outgoing or read 2025-03-03 14:39:40 +01:00
Sylvain Berfini
7d6c50cf29 Renamed some layouts 2025-03-03 14:24:19 +01:00
Sylvain Berfini
8b9ceef6da Updated firebase BoM 2025-03-03 11:46:54 +01:00
Sylvain Berfini
12e6de52a8 Fixed inserting emoji/digit at the current cursor position instead of at the end 2025-03-03 10:26:17 +01:00
Sylvain Berfini
990549eb24 Prevent going back to operation in progress dialog after hanging up started group call, also go back to calls history 2025-02-27 14:48:28 +01:00
Sylvain Berfini
4afa2ebc93 Updated dependencies 2025-02-27 10:21:30 +01:00
Sylvain Berfini
e05d4cf94a Prevent crash when sharing file from native gallery due to chatRoom not being initialized yet (but it is not needed) 2025-02-27 09:58:54 +01:00
Sylvain Berfini
01c079440d Do not follow telecom manager audio routing requests if not connected to Android Auto and concerns Speaker or Earpiece 2025-02-26 11:21:14 +01:00
Sylvain Berfini
8eda500dae Prevent login issue if SIP identity is given instead of username in third party account login 2025-02-25 20:49:43 +01:00
Sylvain Berfini
a392cf3a6b Prevent crash first time app is installed due to chat message notification sound not installed yet 2025-02-25 19:27:16 +01:00
Sylvain Berfini
977fb63693 Add domain to third party SIP account auth info 2025-02-25 14:06:53 +01:00
Sylvain Berfini
fcd365ad81 Play incoming chat sound file when a message is being received in currently opened conversation 2025-02-25 11:29:23 +01:00
Sylvain Berfini
f1b23337e0 Prevent connexion in progress when going back to waiting room 2025-02-25 09:27:17 +01:00
Sylvain Berfini
296a324ba3 Prevent call in state IDLE to be added to TelecomManager, wait for IncomingReceived or OutgoingProgress 2025-02-24 12:16:05 +01:00
Sylvain Berfini
7e2a4c124d Improved UI of assitant & welcome pages, moving mountains at the bottom like on desktop 2025-02-19 15:25:28 +01:00
Sylvain Berfini
b8a6177f97 Fixed password text fields not using the right font when in visible mode & for hint text 2025-02-19 14:15:26 +01:00
Sylvain Berfini
f3991fb29d Small improvements for 7 inches screen in portrait 2025-02-19 13:51:27 +01:00
Sylvain Berfini
0bf6d60570 Prevent crash if MediaPlayer can't be instancianted 2025-02-18 17:00:52 +01:00
Sylvain Berfini
ef79475525 Fixed resend menu not visible when message goes into error state unless conversation is left & opened again 2025-02-18 16:34:27 +01:00
Sylvain Berfini
ec9c7bd070 Bumped AGP to 8.8.1 2025-02-18 12:02:57 +01:00
Sylvain Berfini
875198164d Added back auto export media to native gallery feature 2025-02-18 11:47:33 +01:00
Sylvain Berfini
17511a4c26 Refactored app to use chatRoom.identifier from SDK instead of computing it from local & peer address 2025-02-17 15:23:10 +00:00
Sylvain Berfini
ca08ed68be Updated coil to 3.1.0 2025-02-17 09:27:09 +01:00
Sylvain Berfini
e292b0d7e8 Fixed some scenario where account(s) reload isn't done 2025-02-13 14:17:28 +01:00
Sylvain Berfini
8c1889b181 Fixed duplicated listeners when doing remove provisioning 2025-02-13 11:59:59 +01:00
Sylvain Berfini
037cd71814 Improved logs 2025-02-13 11:46:43 +01:00
Sylvain Berfini
40e9dfc522 Fixed small UI issue if SIP URI is too long 2025-02-13 11:33:44 +01:00
Sylvain Berfini
7e0c6a23a9 Added hourglass icon for chat message in delivery pending state 2025-02-13 11:29:26 +01:00
Sylvain Berfini
7ccd42580d Handle remote provisioning failure from QR Code assistant 2025-02-13 11:01:00 +01:00
Sylvain Berfini
a238ae8db0 Bumped dependencies 2025-02-13 10:19:40 +01:00
Sylvain Berfini
a210ea67c1 Prevent choose address of phone number dialog not being dismissed once a user made a choice 2025-02-13 09:22:06 +01:00
Sylvain Berfini
dd454113e8 Prevent display issue if account has an empty display name 2025-02-12 10:13:23 +01:00
Sylvain Berfini
690140c2b8 Prevent attach file icon hidden when creating conversation using keyboard 2025-02-12 09:58:23 +01:00
Sylvain Berfini
ff323cea68 Also prevent issue if device has no speaker 2025-02-12 09:44:49 +01:00
Sylvain Berfini
fede808df1 Fixed bluetooth audio device switch while on tablet 2025-02-10 12:03:29 +01:00
Sylvain Berfini
ba5786fa0a Prevent sending multipart SIP message in basic chat rooms 2025-02-10 10:55:18 +01:00
Sylvain Berfini
22b447a67f Using supported tags for third party accounts 2025-02-10 10:23:33 +01:00
Sylvain Berfini
ea79e9243d Changed account login wording to ask for username instead of SIP URI 2025-02-10 09:35:30 +01:00
Sylvain Berfini
709f7dd3c5 Fixed missing chat room if you created one and sent a message in it 2025-02-07 17:12:18 +01:00
Sylvain Berfini
9381b459a0 Removed participant joining label in active speaker miniature (no room for it) 2025-02-07 16:31:07 +01:00
Sylvain Berfini
7bf9eb8394 Prevent starting a group call while already in call 2025-02-07 15:53:32 +01:00
Sylvain Berfini
30fc60c0ef Show video preview in calls list & conference participants list fragments 2025-02-07 15:34:10 +01:00
Sylvain Berfini
3e5a0c22f8 Forgot to do the same for permissions layout 2025-02-07 14:16:44 +01:00
Sylvain Berfini
2b75ecdaca Reworked assistant screens for tablets 2025-02-07 13:50:26 +01:00
Sylvain Berfini
6afd711539 Reworked welcome screens 2025-02-07 10:58:36 +01:00
Sylvain Berfini
bfc435c350 Ask user confirmation for cancelling meeting if it us the organizer, to delete it if it's only a participant 2025-02-07 09:29:52 +00:00
Sylvain Berfini
0f59e1a381 Prevent crash if for some reason today is not found 2025-02-06 17:16:24 +01:00
Sylvain Berfini
dd7546484b Fixed in-call bottom sheets not intercepting clicks 2025-02-06 09:44:45 +01:00
Sylvain Berfini
86b35354c3 Increased dialpad touch area 2025-02-06 09:34:57 +01:00
Sylvain Berfini
3e91f3e5ff Added advanced setting to choose between point to point and end to end encryption when creating a meeting or a group call 2025-02-04 15:43:57 +01:00
Sylvain Berfini
74394445c9 Improved toast API 2025-02-04 13:53:49 +01:00
Sylvain Berfini
536667cfe1 Prevent attach file button in conversation to be disabled after sending max number of attachments 2025-02-04 13:14:33 +01:00
Sylvain Berfini
b057163b43 Removed useless back button 2025-02-03 17:19:18 +01:00
Sylvain Berfini
63051ae58e Show who terminated a call in fragment title 2025-02-03 16:55:01 +01:00
Sylvain Berfini
00a7d34509 Fixed call history list filter not using resolved contact name 2025-02-03 16:33:11 +01:00
Sylvain Berfini
da385ee6e1 Added short toast when user starts recording a call 2025-02-03 16:06:15 +01:00
Sylvain Berfini
2952b2db01 Updated CHANGELOG 2025-02-03 14:13:34 +01:00
Sylvain Berfini
2ed4f44e50 Fixed group call missed notification & in-call alert titles 2025-02-03 14:01:30 +01:00
Sylvain Berfini
1c09388266 Disable username/password fields when account has been created (until SMS is sent), fixed default values not loaded 2025-02-03 13:41:54 +01:00
Sylvain Berfini
e5795ea05f Prevent crash if composing is sent when chat room not initialized yet 2025-02-03 09:43:17 +01:00
Sylvain Berfini
0625239477 Conference listener onActiveSpeakerParticipantDevice signature has changed 2025-01-30 10:59:21 +01:00
Sylvain Berfini
5e6186d115 Bumped dependencies 2025-01-30 09:48:51 +01:00
Sylvain Berfini
28998d4463 Fixed total unread message count if deleted chat room contained unread messages 2025-01-29 15:43:19 +01:00
Sylvain Berfini
6121040bda Fixed missing conversations until tab was left & opened again 2025-01-29 15:38:45 +01:00
Sylvain Berfini
0f3aea191f Improved Telecom Call Control Callback disconnect causes 2025-01-29 10:56:37 +01:00
Sylvain Berfini
e9385b5c07 Removed debug APK from GitLab artefacts and reduced artefacts expire 2025-01-29 09:27:34 +01:00
Sylvain Berfini
ec68e931c4 Fixed contacts not reladed when bodyless friendlist presence is received 2025-01-28 17:45:09 +01:00
Sylvain Berfini
37c23066f0 Fixed missing composing notifications 2025-01-28 16:55:49 +01:00
Sylvain Berfini
a5b8a8a683 Show git describe in startup logs 2025-01-27 17:03:47 +01:00
Sylvain Berfini
6d08625168 Fixed group call missed notification & in-call alert titles 2025-01-27 10:45:06 +01:00
Sylvain Berfini
b4329af83a Fixed operation in progress dialog not displayed when creating group call 2025-01-27 10:33:05 +01:00
Sylvain Berfini
de5f44ba04 Updated contacts when CardDAV sync is done 2025-01-27 09:23:42 +01:00
Sylvain Berfini
e686ed90e9 Updated participants label with count 2025-01-23 11:26:44 +01:00
Sylvain Berfini
f5ba48b9f0 No longer use conferenceScheduler for creating group calls 2025-01-23 09:22:20 +01:00
Sylvain Berfini
4dd92cddf0 Fixed issue with save/restore video preview position 2025-01-22 11:35:41 +01:00
Sylvain Berfini
1ead4d9218 Use call's startdate for system notification timestamp 2025-01-22 11:09:19 +01:00
Sylvain Berfini
6a41f7f67d Another attempt at preventing an empty fragment to be visible sometimes when going back from a conversation 2025-01-22 10:25:09 +01:00
Sylvain Berfini
febc6a85d3 Trying to prevent hidden bottom nav bar (very rare) 2025-01-22 10:12:34 +01:00
Sylvain Berfini
e1bcae703c Fixed meeting participants list not showing changes if joined right after 2025-01-22 09:53:17 +01:00
Sylvain Berfini
d05da148e7 Improved permission request in-app when it has already been denied once 2025-01-21 17:34:13 +01:00
Sylvain Berfini
ec9984c86b Show if audio codec is mono or stereo 2025-01-21 11:44:21 +01:00
Sylvain Berfini
456181609b Added shortcut to Android app settings in advanced params 2025-01-21 11:31:08 +01:00
Sylvain Berfini
f7d65b102e Fixed mistake in previous commit 2025-01-21 10:43:02 +01:00
Sylvain Berfini
06d1ae81b5 Prevent empty screen showing up sometimes when leaving a conversation 2025-01-21 10:02:51 +01:00
Sylvain Berfini
8477980011 Removed findNativeContact, it shouldn't be necessary and will prevent useless queries 2025-01-20 14:58:44 +01:00
Sylvain Berfini
e283b7b48e Prevent having two or more ZRTP SAS dialog stacked 2025-01-20 14:55:53 +01:00
Sylvain Berfini
fa9bcd3475 Show toast if record_audio or camera permission is missing while in call, if request permission fails open the android app settings 2025-01-20 12:28:02 +01:00
Sylvain Berfini
7f49a7756c Prevent crash in case of exception while creating the MasterKey in VFS 2025-01-20 09:50:02 +01:00
Sylvain Berfini
e7a4a24eaf Wait 500ms before reloading sound devices 2025-01-20 09:23:05 +01:00
Sylvain Berfini
e938105db0 Fixed settings french string 2025-01-18 19:58:32 +01:00
Sylvain Berfini
ca7035d84a Hide dynamically created empty chat rooms 2025-01-16 17:17:11 +01:00
Sylvain Berfini
27b4fa63f9 Prevent meeting schedule end time to change (when changing start time) if user set it manually 2025-01-16 14:58:11 +01:00
Sylvain Berfini
e464a49176 Fixed issue in current call conversation lookup 2025-01-16 14:26:07 +01:00
Sylvain Berfini
15f9d04747 Fixed back navigation when in conversation opened from meeting call history 2025-01-16 13:56:58 +01:00
Sylvain Berfini
9504c6d1ca Prevent show empty conversations as end to end encrypted and trusted 2025-01-16 13:49:19 +01:00
Sylvain Berfini
d0c733e81b Prevent huge list area when not required 2025-01-16 11:59:16 +01:00
Sylvain Berfini
e85c97837f Let user try to open file as plain text if no app on the device is registered to handle MIME type (useful for logs file without extension) 2025-01-16 11:08:00 +01:00
Sylvain Berfini
375c020b9b Added strings for conference security events 2025-01-16 09:56:24 +01:00
Sylvain Berfini
fee595cfdc Asking for EndToEnd encryption security level when scheduling a meeting/creating a group call if LIME is available 2025-01-16 08:25:05 +00:00
Sylvain Berfini
dbb1793ea0 Bumped dependencies 2025-01-15 20:52:35 +01:00
Sylvain Berfini
4b0fcd38d7 Reworked dialog confirmation model 2025-01-15 20:34:02 +01:00
Sylvain Berfini
3a3518b2a5 Do not disable file attachments in conversation attached to a call, added navigation to file viewer from in-call conversation 2025-01-15 18:36:11 +01:00
Sylvain Berfini
788dd338fb Fixed two meetings showing today's indicator in list 2025-01-15 16:18:09 +01:00
Sylvain Berfini
2e89f4d101 Show toast & un-toggle setting when VFS fails to be enabled + show dialog to confirm before turning it on 2025-01-15 16:05:54 +01:00
Sylvain Berfini
8b183fd347 Set LIME algo to c25519 2025-01-15 13:30:08 +01:00
Sylvain Berfini
815a2ed854 Fixed color issue 2025-01-15 11:31:08 +01:00
Sylvain Berfini
2ba54f085d Make sure foreground services notifications ID are re-set when service is destroyed 2025-01-15 10:22:27 +01:00
Sylvain Berfini
ae80e45e1c Fixed typo 2025-01-14 14:25:22 +01:00
Sylvain Berfini
7967a97b01 Updated ZRTP key agreements allowed algorithms 2025-01-14 11:18:12 +01:00
Sylvain Berfini
d5e20c02fe Fixed deadlock 2025-01-13 14:22:58 +01:00
Sylvain Berfini
00fbfcc490 Added early media advanced settings + added video views in incoming/outgoing call fragments to see/capture early media video if any 2025-01-13 12:29:34 +00:00
Sylvain Berfini
17e3622bdc Try to enter PiP mode when leaving CallActivity during a video call, either by back gesture or button, and remove PiP when call is ended 2025-01-13 12:08:19 +01:00
Sylvain Berfini
6b7591c971 Make link clickable 2025-01-13 10:12:49 +01:00
Sylvain Berfini
633fa07164 Fixed outline in landscape 2025-01-13 10:01:45 +01:00
Sylvain Berfini
fbe5885d08 Updated numpad to show letters 2025-01-11 14:15:12 +01:00
Sylvain Berfini
099350244a Added numpad floating action button 2025-01-11 10:54:29 +00:00
Sylvain Berfini
eac72a0e42 Do not show call ended fragment if accepted/declined on another device 2025-01-11 11:19:31 +01:00
Sylvain Berfini
de29fbc125 Updated gradle version 2025-01-11 10:50:46 +01:00
Sylvain Berfini
864677dab1 Do not use first address for global contact avatar model 2025-01-10 11:21:38 +01:00
Sylvain Berfini
c500761940 Keep screen on while playing media + fixed log file not opened if no extension is set for the file 2025-01-10 10:50:33 +01:00
Sylvain Berfini
e7f888ad78 Forgot to set the call notification color (setColorized is set to true) 2025-01-09 10:00:36 +01:00
Sylvain Berfini
32a3feef42 Improved tertiary button disabled style 2025-01-08 13:13:32 +01:00
Sylvain Berfini
e379833e18 Prevent race condition where service notification for FileTransferService would not be properly configured 2025-01-08 09:59:45 +01:00
Sylvain Berfini
486706299d Harmonized margins 2025-01-08 09:03:08 +01:00
Sylvain Berfini
b1a03db96f Added setting in CorePreferences to hide phone numbers from contacts (also disables contacts list filter) 2025-01-07 11:10:51 +01:00
Sylvain Berfini
5cee11c779 Show label when devices list in account profile is empty 2025-01-06 16:33:37 +01:00
Jonathan Bartet
a9736df3eb Update french file strings.xml 2025-01-06 15:41:37 +01:00
Sylvain Berfini
da848ecc61 Ignore unmute requests from telecom manager unless user is connected to Android Auto 2025-01-02 09:07:07 +00:00
Sylvain Berfini
942e78eede Fix chat message file transfer status when leaving & going back into conversation 2024-12-24 15:29:41 +01:00
Sylvain Berfini
b084807d18 Disable video toggle button and show indeterminate spinner while re-INVITE is in progress 2024-12-23 20:04:05 +01:00
Sylvain Berfini
d0dc42c67b Fixed text file colors in outgoing messages while in dark mode, fixed disabled in-call button color issue 2024-12-20 14:32:29 +01:00
Sylvain Berfini
83f249b1d9 Fixed contacts from remote LDAP/CardDAV not shown in contacts list when doing a search 2024-12-20 11:12:59 +01:00
Sylvain Berfini
8f52182959 Fixed file transfer in chat if file was picked from generic file picker 2024-12-19 16:56:10 +01:00
Sylvain Berfini
2c9a15a007 Removed track stop & gap from recording & media players 2024-12-19 15:39:08 +01:00
Sylvain Berfini
5d96ea3b56 Prevent 'media not found' error if it isn't found in conversation media list, in that case open it but you won't be able to swipe to other media files 2024-12-19 15:37:42 +01:00
Sylvain Berfini
e1c4fc2525 Fixed other accounts top bar notification background color in dark mode 2024-12-19 13:48:23 +01:00
Sylvain Berfini
049f63b61e Added icon next to last message in conversations list depending on it's content 2024-12-19 13:39:49 +01:00
Sylvain Berfini
eebc1bc91e Moved third party sip account login auth id field to advanced settings, added outbound proxy advanced setting 2024-12-19 12:12:03 +01:00
Sylvain Berfini
0d5bf5ed3e Fixed date picker in meeting scheduler 2024-12-19 08:42:25 +00:00
Sylvain Berfini
bad1621126 Updated jitpack.io maven repository configuration 2024-12-19 09:35:04 +01:00
Sylvain Berfini
8e93fefda3 Small improvements reported by Android Studio code inspector 2024-12-18 15:30:25 +01:00
Sylvain Berfini
ea92ca2220 Removed hardcoded text 2024-12-18 15:10:33 +01:00
Sylvain Berfini
3734c8ca90 Fixed issue when trying to add a participant to an existing group (meeting, conversation, etc...) 2024-12-18 14:20:16 +01:00
Sylvain Berfini
aad50669c4 Use Rejected instead of Error call disconnect cause because otherwise it triggers an exception on some devices 2024-12-18 13:56:40 +01:00
Sylvain Berfini
679f125870 Reworked meeting scheduler, now correctly handles selected date & time in local timezone to the one selected if different 2024-12-18 13:11:50 +01:00
Sylvain Berfini
a90dd53c9c Fixed dumb code 2024-12-18 09:07:06 +01:00
Sylvain Berfini
e664e1802c Fixed issue in chat bubble if more than 4 media files in single bubble 2024-12-17 16:37:43 +01:00
Sylvain Berfini
20fc177c7a Prevent changing timezone in meeting scheduler to update selected time 2024-12-17 10:40:16 +01:00
Sylvain Berfini
7c755bd080 Fixed issue with TelecomManager not disconnecting when call state is Error 2024-12-17 10:10:54 +01:00
Sylvain Berfini
372a16d3ab Fixed dark mode issue with participants list in conference + added menu to share meeting link 2024-12-17 10:10:27 +01:00
Sylvain Berfini
d4b0356bda Fixed deadlock causing ANR because SDK method was called directly in adapter 2024-12-16 16:39:26 +01:00
Sylvain Berfini
31b9836cc0 Reworked DTMFs dialpad in call to show letters 2024-12-16 15:52:41 +01:00
Sylvain Berfini
0571862142 Fixed today text color (in meetings) in dark mode 2024-12-16 15:37:21 +01:00
Sylvain Berfini
0497237630 More contrast improvements 2024-12-16 15:26:52 +01:00
Sylvain Berfini
75905c1321 Fixed tertiarty buttons disabled color 2024-12-16 15:12:37 +01:00
Sylvain Berfini
676a2c7710 Improved contrast for generated avatars in dark mode 2024-12-16 14:52:53 +01:00
Sylvain Berfini
967fb0563d Prevent outgoing calls to be routed on speaker while ringing 2024-12-16 14:36:52 +01:00
Sylvain Berfini
3fb5d8a97b Fixed circle buttons pressed color same as default in dark mode 2024-12-16 14:17:11 +01:00
Sylvain Berfini
4682a75049 Fixed wrong ID in layouts 2024-12-16 12:01:28 +01:00
Sylvain Berfini
466395a34b Added + icon under 0 in dialpad, changed background color in dark mode 2024-12-16 11:29:42 +01:00
Sylvain Berfini
2380c0fa6d Color improvements in dark mode for bottom sheets 2024-12-16 11:07:30 +01:00
Sylvain Berfini
137e8941cb Expand contact's devices for trust by default unless at 100% 2024-12-16 09:33:41 +00:00
Sylvain Berfini
409baab7c4 Fixed another typo 2024-12-16 09:14:38 +00:00
Sylvain Berfini
f068b5c9ce Prevent crash if identity address couldn't be parsed 2024-12-16 10:03:45 +01:00
Sylvain Berfini
3cdcc4bb77 Fixed splashscreen color in dark mode & window background color for bottom system bar, other color related improvements 2024-12-16 09:54:45 +01:00
Sylvain Berfini
a3ed13bc79 Show starred contacts first in lists 2024-12-12 15:58:53 +01:00
Sylvain Berfini
3a23c8813e Fixed conversation header invisible in landscape, updated missing separator color 2024-12-12 15:47:13 +01:00
Sylvain Berfini
9d58ced715 Set IMDN threshold to 1 2024-12-12 14:50:10 +01:00
Sylvain Berfini
86e2c731da Updated UI when contact no trusted device 2024-12-12 14:01:06 +01:00
Sylvain Berfini
5593e06cf5 Fixed typos 2024-12-12 12:00:33 +01:00
Sylvain Berfini
7bfed48b23 Updated some colors, no longer need to block material components version to an old release 2024-12-12 10:41:22 +01:00
Sylvain Berfini
821986b6a9 Prevent no default account being set, using first available account 2024-12-12 10:24:52 +01:00
Sylvain Berfini
b9065d3c7c Bumped dependencies, fixed back gesture when adding participants to a conversation or an existing meeting 2024-12-12 09:46:43 +01:00
Sylvain Berfini
47a308dcfc Fixed color issue in chat message forward fragment 2024-12-11 20:17:16 +01:00
Sylvain Berfini
9f6fb13e80 Another batch of fixes related to colors 2024-12-11 17:13:12 +01:00
Sylvain Berfini
6d3ef66995 More color fixes 2024-12-11 16:52:33 +01:00
Sylvain Berfini
e57ff021e9 Fixed chat message edit text field background color 2024-12-11 16:38:34 +01:00
Sylvain Berfini
d5ca4c29d3 Fixed broken avatar generation for notifications & shortcuts 2024-12-11 16:26:14 +01:00
Sylvain Berfini
b3da6ed347 Fixed issue with conference participants devices when admin status of one participant changes 2024-12-11 16:20:03 +01:00
Sylvain Berfini
b6e36272f7 Some fixes related to recent color/layout changes 2024-12-11 16:06:39 +01:00
Sylvain Berfini
41081f565a Reworked colors for better dark theme 2024-12-11 15:06:47 +01:00
Sylvain Berfini
b172396f0d Updated empty recordings list illustration 2024-12-11 11:51:16 +01:00
Sylvain Berfini
95b6a8e7a5 Improved some texts 2024-12-11 11:03:27 +01:00
Sylvain Berfini
d6ce9250fc Improved pressed effect in contact actions as well 2024-12-11 10:41:57 +01:00
Sylvain Berfini
0e057d8aa4 Improved press effect on conversation info actions 2024-12-11 10:39:06 +01:00
Sylvain Berfini
e0c58c0ac5 Added menu to go to shared media/documents from conversation details, only startPostponedEnterTransition() in doOnPreDraw for RecyclerView fragments 2024-12-10 20:28:19 +01:00
Sylvain Berfini
4304552bc8 Updated Ktlint 2024-12-10 12:20:52 +01:00
Sylvain Berfini
ab1d271b76 Updated coil from 2.7.0 to 3.0.4 2024-12-10 10:46:06 +01:00
Sylvain Berfini
b5b57405c6 Updated links that redirects to linphone.org new website 2024-12-10 10:06:09 +01:00
Sylvain Berfini
6476bb518d Added sub menu for file picker in conversation to allow picking files other than media 2024-12-09 16:44:02 +01:00
Sylvain Berfini
f9cf90fecd Show notification top bar when MWI messages are waiting 2024-12-09 14:37:58 +01:00
Sylvain Berfini
e5f227ba35 Do not list ourselves in group conversation participants if we have left, reworked disabled/encrypted conversation info box 2024-12-09 13:54:08 +01:00
Sylvain Berfini
d718cff486 Fixed wrong icon in account profile 2024-12-09 11:33:52 +01:00
Sylvain Berfini
3167544873 Added an option to copy conversation participant SIP URI from menu 2024-12-06 10:49:45 +01:00
Sylvain Berfini
af4ff25310 Fixed outgoing chat bubble background color in dark mode when using another theme than the default one 2024-12-06 09:05:35 +01:00
Sylvain Berfini
75bb28cb2f Recreate activity when theme is changed through remote provisioning, fixed keep alive service automatically enabled after remote provisioning 2024-12-05 16:52:20 +01:00
Sylvain Berfini
935d463896 Prevent last message sender name in front of composing label in chat rooms list cells + prevent network unreachable alert during core startup 2024-12-05 12:10:54 +01:00
Sylvain Berfini
c988504319 Clear previous grammar files from local storage 2024-12-05 11:45:57 +01:00
Sylvain Berfini
5bcf0e8ddb Added try/catch around some nav methods 2024-12-05 10:57:48 +01:00
Sylvain Berfini
a5f846a26d Added file sharing server URL in advanced settings 2024-12-04 13:49:54 +01:00
Sylvain Berfini
877565e516 Display media in square grid if exactly 4 of them 2024-12-04 11:23:52 +01:00
Sylvain Berfini
3a7265295e Fixed chat message file upload indicator 2024-12-03 22:16:35 +01:00
Sylvain Berfini
46dc7355b2 Replaced old URLs by new ones 2024-12-03 15:06:30 +01:00
Sylvain Berfini
d80b023918 Bumped dependencies 2024-12-03 14:47:23 +01:00
Sylvain Berfini
c4eb74ff0d Updated maven repository address 2024-12-03 14:42:13 +01:00
Sylvain Berfini
f48da167ab Protect navigate() calls 2024-12-03 14:38:44 +01:00
Sylvain Berfini
6b11e37b14 Make sure newly added account is marked as default in side menu 2024-12-02 11:25:30 +01:00
Sylvain Berfini
5a1487a691 Fixed call actions bar size & margins 2024-12-02 10:56:13 +01:00
Sylvain Berfini
badbbde183 Update call logs list when starting/receiving call while on call log details page 2024-12-02 10:31:04 +01:00
Sylvain Berfini
713ca4c1d5 Increased single media max height a bit in chat, renamed info message long press action and changed icon 2024-11-29 09:14:19 +01:00
Sylvain Berfini
d312141eda Fixed issue with media preview in chat bubble 2024-11-28 15:32:13 +01:00
Sylvain Berfini
4f0ca4adca Do not consider chat room as read-only if empty 2024-11-28 13:01:31 +01:00
Sylvain Berfini
a4c897b47d Fixes & improvements for remote contact directory search 2024-11-28 12:59:28 +01:00
Sylvain Berfini
711f2f4200 Fixed overlapped texts on narrow devices with increased text size 2024-11-27 10:07:35 +01:00
Sylvain Berfini
49d8381705 Make sure device name used in user-agent doesn't contains any apostrophe 2024-11-27 09:30:33 +01:00
Sylvain Berfini
0ce886cb56 Also apply foldable UI when in conference + prevent folded UI for non-tabletop orientation 2024-11-26 16:42:36 +01:00
Sylvain Berfini
40541d3316 Update contact when discovered later in call views, try to prevent incoming call notification to be visible when already in incoming call fragment 2024-11-26 13:34:00 +01:00
Sylvain Berfini
11e44d1fc4 Removed code not needed anymore, now done in SDK automatically 2024-11-26 11:14:08 +01:00
Sylvain Berfini
d864b5efa5 Added TURN NAT policy settings 2024-11-26 10:32:34 +01:00
Sylvain Berfini
0b0d7ce85a Updated chat message long press menu 2024-11-26 09:25:24 +01:00
Sylvain Berfini
363ce834fa Improved UI when flip/fold device is half-opened 2024-11-25 16:31:29 +01:00
Sylvain Berfini
8bb6b61edc Prevent crash in landscape on some extreme aspect ratio devices + added landscape version of numpad layout 2024-11-25 15:17:47 +01:00
Sylvain Berfini
2d2a5e26f6 Automatically play next message voice record, if any 2024-11-25 14:55:45 +01:00
Sylvain Berfini
213e62d125 Added remote contact directory lookup if friend isn't found locally 2024-11-25 10:11:52 +01:00
Sylvain Berfini
8d78f2b698 Added back spinner on incoming/outgoing call fragments, removed chronometer 2024-11-22 11:56:45 +01:00
Sylvain Berfini
f3a2020466 Improved call layouts for landscape orientation 2024-11-21 17:27:27 +01:00
Sylvain Berfini
167b42810f Added download progress percentage label 2024-11-21 15:47:02 +01:00
Sylvain Berfini
d71966e77d Search in remote CardDAV contacts if such server is configured 2024-11-20 15:05:25 +00:00
Sylvain Berfini
cf901a9c2b Try to prevent crash seen in Crashlytics 2024-11-20 14:58:16 +01:00
Sylvain Berfini
a743a0d2c6 Prevent crash in activity if dialog can't be shown 2024-11-20 10:33:51 +01:00
Sylvain Berfini
601aaf0b5c Updated README & CHANGELOG 2024-11-20 10:17:59 +01:00
Sylvain Berfini
f65666a996 Improved help & troubleshooting views 2024-11-18 11:38:14 +01:00
Sylvain Berfini
4f5ea3b5a4 Removed TODOs related to friends' refKey 2024-11-18 10:41:48 +01:00
Sylvain Berfini
90971224d5 Fixed ZRTP media encryption label in advanced settings not showing if post quantum or not 2024-11-15 16:11:40 +01:00
Sylvain Berfini
1f08682340 Fixed in-call/conf bottom sheet action label color not matching icon status 2024-11-14 17:30:15 +01:00
Sylvain Berfini
1e4066163f Prevent click on call bottom sheet background to collapse it and start full screen mode (by going through) 2024-11-14 16:27:09 +01:00
Sylvain Berfini
b7e7e08bbe Fixed conference chronometer having two different visibility binders 2024-11-14 13:19:09 +01:00
Sylvain Berfini
eb80a16202 Prevent being stuck in full screen mode without video 2024-11-14 12:44:43 +01:00
Sylvain Berfini
a7fb2ccfec Bumped dependencies 2024-11-14 11:00:23 +01:00
Sylvain Berfini
4afb10d82a Added meeting/group call conversation history in call log 2024-11-14 10:19:00 +01:00
Sylvain Berfini
f74976f563 Show forward icon in front of conversation last message if it has been forwarded 2024-11-13 17:17:50 +01:00
Sylvain Berfini
a96940b94a Fixed conversation subject not pre-filled when scheduling meeting 2024-11-13 16:55:48 +01:00
Sylvain Berfini
3fcbc9bf28 Fixed waiting room not displaying call progress when resumed 2024-11-13 14:21:32 +01:00
Sylvain Berfini
a2bf235656 Fixes issue in previous commit 2024-11-13 13:37:20 +01:00
Sylvain Berfini
40093d77cf Improved active speaker conference layout a bit 2024-11-13 13:22:40 +01:00
Sylvain Berfini
e205d0ef00 Trying to prevent bottom nav bar being gone sometimes 2024-11-13 10:39:43 +01:00
Sylvain Berfini
416f4ffef0 Use conference.getChatRoom() instead of searching it, enable text stream for scheduled meetings & group calls 2024-11-13 09:37:53 +01:00
Sylvain Berfini
68fe26e05f Keep bundle mode disabled by default for third party SIP accounts 2024-11-07 16:47:19 +01:00
Sylvain Berfini
cda5deb18d Clear single sign on cache file when removing account 2024-11-06 12:23:32 +01:00
Sylvain Berfini
5c8e4bcc22 Minor UI fix to add some space between end of meeting title and end of chat bubble 2024-11-06 12:07:40 +01:00
Sylvain Berfini
70b4a500d2 Lookup for conference's chat room if any, and allow navigating to it if found 2024-11-06 10:38:13 +01:00
Sylvain Berfini
135ca527ee Clicking on already selected contact will remove it from the selection 2024-11-05 17:01:28 +01:00
Sylvain Berfini
c88917ac68 Improved & fixed issues regarding incoming video call notification 2024-11-05 16:33:25 +01:00
Sylvain Berfini
d02ca882bd Disabled use auth info username as SSO login by default 2024-11-05 12:00:39 +01:00
Sylvain Berfini
807a36b54c Fixed UI not updated when kicked out group conversation 2024-11-04 15:25:46 +01:00
Sylvain Berfini
5f6c02e2ca Fixed minor UI issues 2024-11-04 15:04:09 +01:00
Sylvain Berfini
9a33def8a4 Bumped AGP to 8.7.2 2024-11-01 13:08:38 +01:00
Sylvain Berfini
88b4227bbc Auto close search bar when navigating from list into child details 2024-11-01 10:23:08 +01:00
Sylvain Berfini
5da87f598a Fixed MWI icon if no count is provided in the NOTIFY (only yes/no) 2024-10-31 16:32:00 +01:00
Sylvain Berfini
612c8b3301 Bumped dependencies 2024-10-31 10:32:46 +01:00
Sylvain Berfini
6ad5f7573b Fixed auto full screen issue in conference 2024-10-31 10:21:21 +01:00
Sylvain Berfini
2be4f691f2 Added participants count in conversation info 2024-10-29 16:37:43 +01:00
Sylvain Berfini
eeb19846cc Removed code related to all day meetings + fixed duration issue when only modifiying date 2024-10-24 15:18:00 +02:00
Sylvain Berfini
467e029599 Bumped dependencies 2024-10-24 10:18:43 +02:00
Sylvain Berfini
4ff8c7c7eb Added CCMP server URL setting in advanced account params 2024-10-23 23:05:36 +02:00
Sylvain Berfini
cc6ec98846 Reworked video preview in chat messages to prevent broken layout if thumbnail can't be extracted from video (or if picture is not displayable) 2024-10-23 12:53:39 +02:00
Sylvain Berfini
257352927d Migrated deprecated chat room & conference scheduler APIs 2024-10-22 17:00:05 +02:00
Sylvain Berfini
50cb162bd3 Fixed SSO sign-in when using remote provisioning without credentials information available 2024-10-22 16:41:08 +02:00
Sylvain Berfini
e562f1505d Added wake lock at startup 2024-10-22 11:33:36 +02:00
Sylvain Berfini
d5d3cc0bc2 Fixed issue in previous fix preventing opening a conversation from a chat message notification 2024-10-17 15:09:33 +02:00
Sylvain Berfini
d03f94d52a Prevent crash when conversation fragment resumes if viewmodel doesn't have a chatRoom stored 2024-10-17 09:48:00 +02:00
Sylvain Berfini
6edd20e214 Fixed issue in loss rates 2024-10-16 16:11:38 +02:00
Sylvain Berfini
0217ca78a0 Fixed layout issue in chat message ICS bubble for cancelled meeting with a description 2024-10-16 16:08:10 +02:00
Sylvain Berfini
c435852b32 Bumped AGP to 8.7.1 & Kotlin to 2.0.21, removed deprecated experimental k2 but enabled kapt k2 2024-10-16 12:42:43 +02:00
Sylvain Berfini
c6fe442b09 SDK will filter the chat rooms list itself, no need to do it in app 2024-10-15 15:47:12 +02:00
Sylvain Berfini
6d23402001 Added more call stats (loss rate & jitter buffer) 2024-10-15 14:13:03 +02:00
Sylvain Berfini
e3d356765d Merged blind/attended call transfer feature into one 2024-10-15 13:06:57 +02:00
Sylvain Berfini
d446e6d998 Updated behavior of alerts top bar 2024-10-14 12:18:26 +02:00
Sylvain Berfini
157ea2c847 Fixed missing week header in meetings list after deleting first meeting of the week 2024-10-14 10:53:59 +02:00
Sylvain Berfini
97f12b150a Fixed small issue in account settings layout 2024-10-10 15:59:24 +02:00
Sylvain Berfini
6826a51307 Dismiss chat message long press view if visible when doing a back gesture 2024-10-10 15:20:35 +02:00
Sylvain Berfini
8a39529fd1 Code cleanup 2024-10-10 11:18:41 +02:00
Sylvain Berfini
865f3b9692 Added setting to enable/disable logs printing in system logcat 2024-10-10 09:43:14 +02:00
Sylvain Berfini
b0dbfbcc3d Fixed layout 2024-10-08 16:12:59 +02:00
Sylvain Berfini
7d9a3edf31 Fixed positional arguments 2024-10-08 12:27:20 +00:00
Sylvain Berfini
d0ded694f8 Moved quit button to drawer menu, only visible if keep service alive setting is enabled 2024-10-08 14:01:14 +02:00
Sylvain Berfini
899a3ea374 Added voicemail icon below 1 in dialpad & voicemail URI setting in account params to be called when long pressing voicemail icon in dialpad 2024-10-08 10:47:04 +02:00
Sylvain Berfini
27b1cf90c6 Display configured ephemeral messages duration below conversation title (next to icon) 2024-10-07 11:46:38 +02:00
Sylvain Berfini
6d959d489b Updated AGP to 8.7.0 & Gradle to 8.9 2024-10-03 13:22:33 +02:00
Sylvain Berfini
9e08a5a506 Bumped navigation dependency version 2024-10-03 10:25:57 +02:00
Sylvain Berfini
86e09b5dce Actually do not disable earpiece, it works even without telecom manager support 2024-10-03 09:54:29 +02:00
Sylvain Berfini
248a06c8be For some reason, sometimes Telecom Manager Call Control Callback doesn't lists Earpiece as an available endpoint, disabling it when it happens 2024-10-02 16:51:45 +02:00
Sylvain Berfini
e8100e58da Updated firebase BoM version 2024-10-02 14:14:37 +02:00
Sylvain Berfini
10c68a2c28 Fixed another foreground service not started crash 2024-10-01 15:56:55 +02:00
Sylvain Berfini
cfe00d1b61 Added social app category to Manifest 2024-09-30 14:41:38 +02:00
Sylvain Berfini
20daa67ccf Fixed audio device java object instance in log 2024-09-30 13:46:59 +02:00
Sylvain Berfini
42cf8fd89e Fixed 'paused' label briefly visible at top of conference call UI when joining 2024-09-30 12:30:35 +02:00
Sylvain Berfini
bb4e9fdeb5 Hide call encryption in call ended fragment if call wasn't answered 2024-09-30 12:18:18 +02:00
Sylvain Berfini
cd8785855d Fixed quit app when keep alive service is enabled 2024-09-30 11:17:38 +02:00
Sylvain Berfini
fe2a074d0b Fixed foreground service notification lookup 2024-09-30 10:57:25 +02:00
Sylvain Berfini
63cb7d6630 Added confirmation dialog before starting a group call from a group conversation 2024-09-30 10:22:12 +02:00
Sylvain Berfini
eaa498f1ad Fix regarding in-call foreground service notification & other accounts notifications alert 2024-09-30 10:02:11 +02:00
Sylvain Berfini
f6d4f56bbc Fix voice record not playable if displayed before download terminated 2024-09-29 14:34:42 +02:00
Sylvain Berfini
31d92abcdf Update devices list when removing one 2024-09-29 14:16:27 +02:00
Sylvain Berfini
92ec1940e5 Prevent other accounts message notification header to be displayed when a message is received on the default account 2024-09-29 14:06:27 +02:00
Sylvain Berfini
5ebb4ee6ac Updated AGP, trying to fix crash due to disabled crashlytics with latest changes 2024-09-25 19:43:46 +02:00
Sylvain Berfini
a9d11543d8 Fixed build without google_services.json file 2024-09-24 14:59:46 +02:00
Sylvain Berfini
0e451dcc8a Added shortcuts in drawer menu if configured in remote prov 2024-09-19 16:54:37 +02:00
Sylvain Berfini
e74ebb0d81 Bumped dependencies + trigger voice message player more often to have a smoother progress bar 2024-09-19 14:14:05 +02:00
Sylvain Berfini
4f10a2d1fc Fixed Assistant started when setting & applying remote provisioning URI from advanced params 2024-09-19 13:40:10 +02:00
Sylvain Berfini
690f6fa4c2 Fixed rounded corners of main lists not properly applied on old Android versions 2024-09-19 10:13:47 +02:00
Sylvain Berfini
45cfac3ac6 Make shadow a bit lighter 2024-09-18 16:55:42 +02:00
Sylvain Berfini
db2ce910b9 Fixed pre-selected contacts not having the checkmark in participants list edition 2024-09-18 16:28:31 +02:00
Sylvain Berfini
0baceddf27 Fixed 'you have joined the group' chat event for created group icon not matching others + added new icon for admin rights granted event 2024-09-18 16:10:28 +02:00
Sylvain Berfini
fa7486ff36 Fixed issue with forward message 2024-09-18 10:40:08 +02:00
Sylvain Berfini
b2b55305d2 Forgot to change the label of sign out button in the confirmation dialog + hide delete account message for third party accounts 2024-09-17 17:15:28 +02:00
Sylvain Berfini
5eece24d68 Changed delete account into sign out (wording only) 2024-09-17 16:40:42 +02:00
Sylvain Berfini
51dd246971 Prevent external file sharing to attach file in already opened conversation if any 2024-09-17 12:53:18 +02:00
Sylvain Berfini
3190a1869d Added firebase project ID in troubleshooting fragment, updated icons 2024-09-16 11:53:54 +02:00
Sylvain Berfini
2c2beb5725 Fixed duplicated contacts in start call / conversation list 2024-09-16 10:43:50 +02:00
Sylvain Berfini
7b8b92706b Added very slight shadow effect at top of main lists 2024-09-16 09:50:39 +02:00
Sylvain Berfini
8fc3185278 Removed 'all day' switch in meeting scheduling/edit 2024-09-12 11:46:20 +02:00
Sylvain Berfini
96d85027c3 Workaround for first header not displayed when meetings list is initially drawn 2024-09-12 11:41:48 +02:00
Sylvain Berfini
41721e3994 Hide either IMDN or reactions bottom sheet with back gesture instead of directly going back 2024-09-12 10:13:20 +02:00
Sylvain Berfini
1a35a7048d Improved emoji reaction picker accessibility when font size is increased a lot at OS level 2024-09-11 16:20:39 +02:00
Sylvain Berfini
49059d6b3c Added back round corners to main lists in portrait mode (keep round top bar in landscape) + increased space at the bottom of the avatar in top main bar 2024-09-11 15:52:21 +02:00
Sylvain Berfini
c0e8bb6c1a Fixed issue with top margin, specifically visible when in landscape + reduced spaced in drawer layout for phones in landscape 2024-09-11 12:05:58 +02:00
Sylvain Berfini
1ef4e57d9e Reduced voice recording max width for narrow screens 2024-09-10 16:46:00 +02:00
Sylvain Berfini
85a61839bc Fixed display issue with files in chat bubbles on small screens 2024-09-10 14:23:39 +02:00
Sylvain Berfini
30ab0fa827 Enable both SIP INFO & RFC 2833 for DTMFs 2024-09-10 11:10:53 +02:00
Sylvain Berfini
439e115338 Added media encryption & media encryption mandatory advanced settings 2024-09-10 11:07:25 +02:00
Sylvain Berfini
722840f1c5 Hide software echo canceller toggle 2024-09-10 10:40:43 +02:00
Sylvain Berfini
db751efa91 Wait for adapter to contain data before attaching it to recyclerview to prevent loosing scroll position when rotating device 2024-09-09 11:13:13 +02:00
Sylvain Berfini
6c22b1f66d Added mark as read when dismissing message notif setting 2024-09-05 10:22:15 +02:00
Sylvain Berfini
9fc9574369 Added echo canceller calibration & adaptive rate control settings 2024-09-05 10:10:35 +02:00
Sylvain Berfini
ce4fed2197 Bumped dependencies 2024-09-05 09:27:23 +02:00
Sylvain Berfini
e03dcf3f88 Increased touch area for IMDN notifications + hide keyboard when showing IMDN/Emoji bottom sheet 2024-09-04 16:04:02 +02:00
Sylvain Berfini
d434ab7298 Bumped Firebase BoM version 2024-09-04 15:23:55 +02:00
Sylvain Berfini
4673db9e4e Do not automatically enable full screen mode when only our video is being sent 2024-09-03 17:15:21 +02:00
Sylvain Berfini
6893d35a93 Fixed muted state in conversations list cell when updated from conversation 2024-09-03 16:49:33 +02:00
Sylvain Berfini
4cbad5308c Fixed recordings parsing when file extensions have different lengths 2024-09-03 09:52:59 +02:00
Sylvain Berfini
7fb3a6ada3 Use .mka extension instead of .mkv for voice recordings, use .smff instead of mkv for call recordings 2024-09-02 17:03:44 +02:00
Sylvain Berfini
ace4caca3f Fixed incoming/outgoing call header overlapping system status bar 2024-09-02 16:50:00 +02:00
Sylvain Berfini
95c4106bd0 Prevent crash due to calling chatRoom.getLocalAddress() when it's in Instanciated state (because returned value is null) 2024-09-02 16:26:39 +02:00
Sylvain Berfini
c2e1333be1 Cancelling forward event (if any) when leaving fragment 2024-09-02 14:47:46 +02:00
Sylvain Berfini
b4a754db53 Should prevent 'no network' alert over orange background (instead of red) in main activity due to race condition 2024-09-02 14:35:03 +02:00
Sylvain Berfini
49c3e68f84 Removed useless code (but rotation still brings user to the bottom of messages, don't know why yet) 2024-08-29 17:19:57 +02:00
Sylvain Berfini
3359fbcbd7 Fixed history/conversations list scroll position not retained upon rotation 2024-08-29 17:08:54 +02:00
Sylvain Berfini
6308d66eb6 Fixed in-call numpad not sending DTMFs + prevent keyboard to open when touching numpad area 2024-08-29 17:00:47 +02:00
Sylvain Berfini
4628560411 Fixed meetings list scroll position not retained upon rotation 2024-08-29 16:54:43 +02:00
Sylvain Berfini
84af9437bc Revert picture(s) message summary using emoji 2024-08-29 14:36:44 +02:00
Sylvain Berfini
8a1d88c4b5 Prevent file transfer notification being still displayed after all transfer have ended 2024-08-29 14:19:22 +02:00
Sylvain Berfini
5a7872222d Added advanced account setting to manually update password 2024-08-29 13:43:46 +02:00
Sylvain Berfini
5d7addb8d8 Adding contentIntent to file transfer & push notification services 2024-08-29 11:16:15 +02:00
Sylvain Berfini
bed9288f21 Added device ID setting + fixed some wrong styles 2024-08-29 10:21:01 +02:00
Sylvain Berfini
ed1cf58fd4 Updated time formatter 2024-08-28 21:20:57 +02:00
Sylvain Berfini
a29777eebc Added back kill app button 2024-08-28 12:09:24 +02:00
Sylvain Berfini
1f604d54f2 Automatically enable full-screen mode when enabling video during a call 2024-08-27 17:02:35 +02:00
Sylvain Berfini
245b848c91 Fixed refreshMicrophoneState() that was toggling microphone state 2024-08-27 11:10:36 +02:00
Sylvain Berfini
34e1f0070e Adding back 'hide empty chat rooms' setting, was disabled at some point during 6.0 dev but should be enabled back 2024-08-27 11:07:33 +02:00
Sylvain Berfini
fb9d1c5c0d Prevent staying in assistant when logging in if login was denied once due to invalid password 2024-08-27 10:31:35 +02:00
Sylvain Berfini
b301a5227a Fixed meeting scheduling issues + fixed meetings for today not being displayed 2024-08-27 08:54:55 +02:00
Sylvain Berfini
94f2c1cc98 Fixed text message description not being italic for some parts + improved description: added duration to voice message & replaced file name by image emoji for pictures 2024-08-26 17:13:19 +02:00
Sylvain Berfini
cbaf7673f5 Improved chat bubbles & text color in dark mode 2024-08-26 15:38:18 +02:00
Sylvain Berfini
4d190cabdd Show cancelled meetings in list + fixed scroll to today 2024-08-26 12:03:57 +02:00
Sylvain Berfini
5c77b58154 Prevent case causing memory chat room to be re-used as a diffent one, leading to duplicated listener 2024-08-26 11:05:55 +02:00
Sylvain Berfini
ce1c3dad65 Prevent crash in CoreFileTransferService if Core's thread is busy and doesn't trigger notification fast enough 2024-08-26 10:44:40 +02:00
Sylvain Berfini
4a75315240 Log custom or unexpected types when parsing phone number labels 2024-08-25 10:47:53 +02:00
Sylvain Berfini
317bbb470b Prevent using requireContext() from Core's thead in Fragment 2024-08-23 09:24:49 +02:00
Sylvain Berfini
d8d424d446 Updated README 2024-08-22 15:36:19 +02:00
Sylvain Berfini
ff81a1c615 Bumped emoji picker version, adds skintone picker by long pressing emoji 2024-08-22 11:05:07 +02:00
Sylvain Berfini
769006b043 Added app's versionCode before git describe in troubleshooting fragment 2024-08-22 11:04:00 +02:00
Sylvain Berfini
c40c15b66f Added undertermined progress bar while fetching devices list in account profile 2024-08-22 11:03:21 +02:00
Sylvain Berfini
7c3bed7dd4 Fixed attach file button visible while recording a voice message 2024-08-21 18:44:53 +02:00
Sylvain Berfini
674fa1f41b Catch exception when posting a navigation task on main thread 2024-08-21 10:36:26 +02:00
Sylvain Berfini
ab1ea3392d Fixed duplicated phone numbers in loaded contacts from native addressbook + improved code by factorizing some parts of it 2024-08-21 10:23:53 +02:00
Sylvain Berfini
2821d4b72f Fixed meeting schedule using 'all day' toggle 2024-08-21 10:01:10 +02:00
Sylvain Berfini
9ed2415d1b Fixed participants devices events in history when using search & go to original message 2024-08-20 17:31:30 +02:00
Sylvain Berfini
859e32e655 Added hidden menus to display account contact address GRUU param & chat room peer address 2024-08-20 16:32:36 +02:00
Sylvain Berfini
f325c5ebbd Prevent calling getCallLogs() if Core isn't started yet 2024-08-20 11:05:16 +02:00
Sylvain Berfini
be849e0c80 Hide send message area while doing a search in a conversation 2024-08-20 09:53:16 +02:00
Sylvain Berfini
de9a2318af List all existing & writeable conversations in forward message conversations list 2024-08-19 16:50:31 +02:00
Sylvain Berfini
9ec927c0c4 Fixed issue in chat message reply layout for outgoing bubble 2024-08-19 16:07:01 +02:00
Sylvain Berfini
453b986f82 Fixed meeting list not always displaying week info 2024-08-19 14:19:19 +02:00
Sylvain Berfini
b123082559 Added back config entry to allow disabling native contacts default directory filter 2024-08-19 14:00:10 +02:00
Sylvain Berfini
27298639c3 Reduced high white space in schedule meeting layout at the top 2024-08-19 12:37:23 +02:00
Sylvain Berfini
2c09158977 Added a way to choose which friend list to use to store newly created contacts into (for CardDAV friend list for example) 2024-08-19 12:27:19 +02:00
Sylvain Berfini
bf4ab1b412 Added logs to help debug mic muted button state 2024-08-19 11:32:10 +02:00
Sylvain Berfini
ce7779f720 Do not display the duplicated SIP addresses in contact details 2024-08-19 11:13:17 +02:00
Sylvain Berfini
8c8f15b02d Prevent crash due to un-initialized property 2024-08-17 13:58:25 +02:00
Sylvain Berfini
ded00052b5 Use chatMessage.markAsRead() newly added API 2024-08-14 23:08:58 +02:00
Sylvain Berfini
e0dc53564e Fixed chat attachments preview 2024-08-14 18:25:09 +02:00
Sylvain Berfini
349167868f When scrolling to first unread message of many, remove 1 from counter 2024-08-14 12:55:29 +02:00
Sylvain Berfini
00d14feded Prevent crash when parsing a legacy call recording 2024-08-14 12:01:28 +02:00
Sylvain Berfini
48fd2ba1f8 SDK filters out duplicated SIP addresses & phone numbers now, no need to do it ourselves 2024-08-14 11:40:30 +02:00
Sylvain Berfini
dced2dae7e Improved contacts loader performances 2024-08-14 10:28:39 +02:00
Sylvain Berfini
00dd62553b Various minor improvements 2024-08-14 09:34:41 +02:00
Sylvain Berfini
a855c569fb Fixed scrolling up to original message from reply when it's not in the currently loaded history + hide keyboard when displaying message long press menu 2024-08-13 16:45:43 +02:00
Sylvain Berfini
2021c5e102 Added white background for PDF viewer (in case PDF background is transparent...) 2024-08-13 16:13:09 +02:00
Sylvain Berfini
bd93f0ed71 Bumped dependencies 2024-08-13 15:46:52 +02:00
Sylvain Berfini
5889389866 Keep pick file button instead of emoji picker in conversation while the keyboard is opened 2024-08-13 15:32:01 +02:00
Sylvain Berfini
2104d79e1c Prevent duplicated chat rooms in conversations list when added/removed/added 2024-08-13 14:59:43 +02:00
Sylvain Berfini
415da6f03d Added logs to help troubleshoot release APK signing 2024-08-13 12:11:28 +02:00
Sylvain Berfini
1d0b5a5d4d Build & upload nightly APK as release instead of debug 2024-08-13 10:23:53 +02:00
Sylvain Berfini
e5c7fa07cc Commented out Android Auto calling features for now as they are only available in private beta and we're not part of it yet 2024-08-13 09:50:38 +02:00
Sylvain Berfini
9c40709666 Removed the possibility to have a second chance at ZRTP SAS validation 2024-08-12 17:07:19 +02:00
Sylvain Berfini
4978c9a16d Close search/filter bar with back gesture/button 2024-08-12 16:49:25 +02:00
Sylvain Berfini
196a010f36 Prevent on some devices (such as Pocophone F1) display issue with keyboard & window insets 2024-08-12 15:56:01 +02:00
Sylvain Berfini
b58a23b60d Fixed Android Auto favorites no having generated avatar if no picture available + make them round 2024-08-12 14:37:37 +02:00
Sylvain Berfini
12c112fa39 Added favorites contacts grid in Android Auto UI 2024-08-12 13:43:30 +02:00
Sylvain Berfini
3d8bc10499 Revert changes to Manifest, only add min api car level for actual templated app 2024-08-12 12:15:16 +02:00
Sylvain Berfini
fdee7e618e Added min car level API to Manifest just in case 2024-08-12 11:52:41 +02:00
Sylvain Berfini
5ce7a5524a Added custom settings to directly go to third party sip account login & pre-fill some fields 2024-08-12 10:54:49 +02:00
Sylvain Berfini
ef32dac910 Prevent 'waiting for encryption' label in ended call screen if outgoing call was terminated before other end has answered it 2024-08-09 09:35:33 +02:00
Sylvain Berfini
ea49d3a411 Fixed missing contacts' avatars in chat room shortcuts 2024-08-08 20:25:34 +02:00
Sylvain Berfini
6280ed5f3d Fixed IM encryption mandatory setting that wasn't having any effect anymore 2024-08-08 20:03:33 +02:00
Sylvain Berfini
51d9b18c1c Added missing foreground service notification for outgoing call 2024-08-08 19:47:13 +02:00
Sylvain Berfini
6746e71197 Update call UI to reflect mic muted state when toggled from Android Auto 2024-08-08 19:38:51 +02:00
Sylvain Berfini
57f3b0c78b Added missing auth id field in third party account login form 2024-08-07 17:18:01 +02:00
Sylvain Berfini
455db9b9eb Added a setting to have the dialpad automatically showing up (disabled by default) 2024-08-07 17:12:15 +02:00
Sylvain Berfini
c8d9248e0c Close any expanded bottom sheet in call/conference fragment instead of leaving is user click/gesture back (except for back arrow at top) 2024-08-07 17:06:08 +02:00
Sylvain Berfini
a7f868fe15 Copy app/SDK version when clicking on it 2024-08-07 16:36:27 +02:00
Sylvain Berfini
59aa036875 Hide any message bottom sheet when leaving conversation 2024-08-07 15:40:08 +02:00
Sylvain Berfini
1c8a376c7f Also improved account devices when system display & text size is increased by user 2024-08-07 15:19:52 +02:00
Sylvain Berfini
1ee5993624 Improved help UI when system display & text size is increased by user 2024-08-07 15:09:06 +02:00
Sylvain Berfini
247788c64c Added a setting to choose if we want to use the SIP address username as SSO username 2024-08-07 14:21:41 +02:00
Sylvain Berfini
505fa3b66c Go to assistant when last account has been removed 2024-08-07 11:33:09 +02:00
Sylvain Berfini
a676c51401 Finally managed to make call notifications visible on Android Auto! 2024-08-07 10:00:10 +02:00
Sylvain Berfini
8e588bf800 Added missing pending intent for keep alive service 2024-08-06 16:15:16 +02:00
Sylvain Berfini
f29f5f9bc7 Fixed our own avatar not updated in meetings & conversations after being changed 2024-08-06 15:46:59 +02:00
Sylvain Berfini
a136b7da8b Added missing start at boot feature 2024-08-06 11:39:05 +02:00
Sylvain Berfini
bc38ff19b6 Added more space around list cells 2024-08-06 10:48:56 +02:00
Sylvain Berfini
9e3a7055fe Fixed contact avatars in group chat room messages after reload 2024-08-06 09:43:22 +02:00
Sylvain Berfini
36cc74956e Various fixes from Crashlytics reported issues 2024-08-05 15:31:29 +02:00
Sylvain Berfini
6c20ac8d40 Fixed upload progress indicator not visible when uploading only one file 2024-08-05 12:16:40 +02:00
Sylvain Berfini
59ebff6d15 Added IME action for main fragments search bar to close keyboard 2024-08-05 11:36:17 +02:00
Sylvain Berfini
b55d7070b0 Update participant(s) info in conversation when contact(s) changes 2024-08-05 10:59:51 +02:00
Sylvain Berfini
55cd29e710 Using new SDK APIs to improve chat message search in conversation 2024-08-05 10:01:46 +02:00
Sylvain Berfini
8a410fc77f Store selected contacts list filter & show favorites user preferences to re-use them later 2024-07-31 14:49:52 +02:00
Sylvain Berfini
5cc491335c Prevent crashes in case of exception 2024-07-31 09:29:21 +02:00
Sylvain Berfini
e14a142fb7 Fixed repo path in gitlab-ci file 2024-07-30 14:02:19 +02:00
Sylvain Berfini
f3b6627635 Fixed generated APK file name 2024-07-30 13:48:19 +02:00
Sylvain Berfini
3ec942c475 Forgot to add job-upload.yml to included files 2024-07-30 13:27:06 +02:00
Sylvain Berfini
ce5a5c5ddc Restored gitlab CI sign & upload jobs 2024-07-30 12:27:26 +02:00
Sylvain Berfini
a53542c092 Revert change due to API change in SDK that was reverted 2024-07-30 12:05:28 +02:00
Sylvain Berfini
ae59911f0a Do not use color state resource for background (crashes on Android 9), use drawable instead + use direct color attribute when possible 2024-07-29 19:24:50 +02:00
Sylvain Berfini
e29d64a02b Fixed build with latest 5.4 SDK & bumped dependencies 2024-07-29 14:11:16 +02:00
Sylvain Berfini
4c66012372 Fixed video player aspect ratio 2024-07-22 11:12:36 +02:00
Sylvain Berfini
9465593aa6 Fixed full screen switch after device rotation in media viewer 2024-07-22 10:25:37 +02:00
Sylvain Berfini
2394c701cf Added shortcut in call settings to change incoming call ringtone 2024-07-20 18:13:00 +02:00
Sylvain Berfini
0bea45054b Fixed chat bubbles & text going out of screen 2024-07-19 16:16:40 +02:00
Sylvain Berfini
756a83797f Various UI improvements related to emoji reactions 2024-07-19 15:13:19 +02:00
Sylvain Berfini
87f4ebbd4c Do not start keep alive service as foreground yet, wait for it to be started 2024-07-19 12:46:35 +02:00
Sylvain Berfini
5689557487 Also enable Crashlytics for releases 2024-07-19 11:24:28 +02:00
Sylvain Berfini
e85488cf65 Fixed audio not routed to connected bluetooth device during call 2024-07-19 11:16:19 +02:00
Sylvain Berfini
83df46ca8b Remove existing chat message reaction instead of trying to send it again 2024-07-18 17:21:44 +02:00
Sylvain Berfini
1d328f98e3 Fixed margin in conversation header if no back button 2024-07-18 14:59:58 +02:00
Sylvain Berfini
b5b37bd74d Hide 'copy text' from chat message long press menu if message contains no text 2024-07-18 14:50:29 +02:00
Sylvain Berfini
a6b510e536 Fixed 'no meeting today' at the bottom of the screen with header at the top if list is empty 2024-07-18 14:46:34 +02:00
Sylvain Berfini
22795a5284 Improved main screens' top bar 2024-07-18 14:21:02 +02:00
Sylvain Berfini
4c9bdec61e No longer use dialog for chat message long press 2024-07-18 12:26:12 +02:00
Sylvain Berfini
a73e483478 Fixed issue in permissions layout in landscape + post_notifications permission not granted alert when it was in fact granted 2024-07-18 09:50:51 +02:00
Sylvain Berfini
8ce91d1300 Fixed 'waiting for encryption' when leavign & going back to a call 2024-07-16 16:01:59 +02:00
Sylvain Berfini
79ca1d6505 Using newly added API to know if FEC is available in the current call 2024-07-16 09:27:05 +02:00
Sylvain Berfini
7d40ad5ad1 Finished international prefix help dialog 2024-07-15 16:12:06 +02:00
Sylvain Berfini
681c0f22c3 Do not duplicate ConversationFragment.kt for in-call view anymore 2024-07-15 16:12:06 +02:00
Sylvain Berfini
2086fbad66 Fixed chat bubble long press layout issue in full-screen mode 2024-07-15 16:12:05 +02:00
Sylvain Berfini
fb86cd9bcb Show check mark next to selected contacts when creating a group call/conversation 2024-07-15 16:12:05 +02:00
Sylvain Berfini
b9addcf683 Updated schedule meeting layout 2024-07-15 16:12:05 +02:00
Sylvain Berfini
6c757a9637 Finished contact trust explanation dialog 2024-07-15 16:12:05 +02:00
Sylvain Berfini
6cabf0bdf7 Improved empty subject for start/edit group call/conversation 2024-07-15 16:12:05 +02:00
Sylvain Berfini
179a6c39ca Hide mark as read action on conversation if no unread message 2024-07-15 16:12:05 +02:00
Sylvain Berfini
8a92624254 Updated dependencies 2024-07-15 16:12:05 +02:00
Sylvain Berfini
7aebfafde3 Fixed 1-1 conversation having chartoom-xxxx subject 2024-07-15 16:12:05 +02:00
Sylvain Berfini
1cd79b5086 Added FEC in-call stats 2024-07-15 16:12:05 +02:00
Sylvain Berfini
25be5e1814 Updated CHANGELOG 2024-07-15 16:12:05 +02:00
Sylvain Berfini
c7032b6000 Added data sync foreground service when transfering files through chat (upload & download) 2024-07-15 16:12:05 +02:00
Sylvain Berfini
bf1140fb38 Fixed contact that can't be opened in-app because it has no refKey 2024-07-15 16:12:05 +02:00
Sylvain Berfini
e012d45c10 Disable action buttons & hide numbers & SIP addresses fields if contact doesn't contains any 2024-07-15 16:12:05 +02:00
Sylvain Berfini
11eb0199f8 Start CardDAV synchronization when app starts + improved contact edition 2024-07-15 16:12:05 +02:00
Sylvain Berfini
949d870d7e Force contacts reload after adding/removing CardDAV account 2024-07-15 16:12:05 +02:00
Sylvain Berfini
0cfdedc09a Go back after deleting CardDAV config 2024-07-15 16:12:05 +02:00
Sylvain Berfini
d8c406320c Fixed status bar color in task manager 2024-07-15 16:12:05 +02:00
Sylvain Berfini
a278333eb4 Fixed time zone not displayed in meeting details 2024-07-15 16:12:05 +02:00
Sylvain Berfini
0d5b189978 Added unread messages count on conversation icon during call 2024-07-15 16:12:05 +02:00
Sylvain Berfini
e1852f4ae4 Updated edit meeting layout to match schedule meeting one + hidden meeting repeat option as it's not available yet 2024-07-15 16:12:05 +02:00
Sylvain Berfini
964a597aa1 Improved empty list display due to filter not matching any item 2024-07-15 16:12:05 +02:00
Sylvain Berfini
5ae345e794 Fixed time picker in conference scheduler + added time zone picker in schedule meeting UI 2024-07-15 16:12:05 +02:00
Sylvain Berfini
5289dc4824 Fixed issue in contacts list if some share the same SIP address(es) 2024-07-15 16:12:05 +02:00
Sylvain Berfini
f94b57d304 Added Android 15 AppStartupListener to print info 2024-07-15 16:12:05 +02:00
Sylvain Berfini
72fd952a55 Prevent paused by remote label to hide remote call end display name 2024-07-15 16:12:05 +02:00
Sylvain Berfini
811a0466d4 Updated one_account_max to allow to set a different limit 2024-07-15 16:12:05 +02:00
Sylvain Berfini
f449763c4e Improved new conversation insertion in list 2024-07-15 16:12:05 +02:00
Sylvain Berfini
c4fa68858c Fixed display issue if upload logs button is hidden 2024-07-15 16:12:05 +02:00
Sylvain Berfini
77e99fbfd3 Trying to prevent 'network not reachable' alert displayed when it shouldn't + added logs to help debug conferences list 2024-07-15 16:12:05 +02:00
Sylvain Berfini
af3cab475b Fixed missing toasts in conversations list + improved conversation removal (no longer scrolling back to top) 2024-07-15 16:12:05 +02:00
Sylvain Berfini
57f8ff3341 Replaced AccountCreator by AccountManagerServices & using it to list account devices 2024-07-15 16:12:05 +02:00
Sylvain Berfini
54ee456f8e Added missing cancel dialog when deleting a meeting for which we are the organizer from the meetings list + updated strings 2024-07-15 16:12:05 +02:00
Sylvain Berfini
f3eb821946 Fixed stucked in 'wait for encryption' for SRTP & not encrypted calls 2024-07-15 16:12:05 +02:00
Sylvain Berfini
a788191e50 Bumped car dependency version 2024-07-15 16:12:05 +02:00
Sylvain Berfini
94e6b28f4f Fixed ZRTP dialogs missing upon rotation 2024-07-15 16:12:05 +02:00
Sylvain Berfini
ce3c37ad15 Hide zrtp dialog alert try again button after second failed attempt + added landscape layout + fixed security level update on avatar 2024-07-15 16:12:05 +02:00
Sylvain Berfini
af6cfdfc18 Leave start call fragment after successful call && updated name group call / conversation dialog to stay at top of screen 2024-07-15 16:12:05 +02:00
Sylvain Berfini
515b645b89 Using call.callLog.remoteAddress() instead of call.remoteAddress() to have Address' displayName use P-Asserted-Identity info if available 2024-07-15 16:12:05 +02:00
Sylvain Berfini
c6550b6256 Intercept click on background while operation in progress dialog is visible 2024-07-15 16:12:05 +02:00
Sylvain Berfini
e3f1611a0f Prevent unecessary sound devices reload 2024-07-15 16:12:05 +02:00
Sylvain Berfini
08052d64fd Added top bar alert if post notifications permission isn't granted + improved connexion error toast + improved network unreachable alert 2024-07-15 16:12:05 +02:00
Sylvain Berfini
8f34c3ea5c Added dialog when ZRTP SAS token doesn't match or when clicking on 'nothing matches' button 2024-07-15 16:12:05 +02:00
Sylvain Berfini
ceb3679975 New ZRTP SAS validation dialog for cache mismatch scenario 2024-07-15 16:12:05 +02:00
Sylvain Berfini
72f8574a1e Improvements & fixes related to bottom sheets 2024-07-15 16:12:05 +02:00
Sylvain Berfini
ec6316f6e5 Reworked zrtp sas validation dialog layout 2024-07-15 16:12:05 +02:00
Sylvain Berfini
4bd6dc4e0f Improved & factorized call's media encryption icon & label info 2024-07-15 16:12:05 +02:00
Sylvain Berfini
cad05a0c83 Targetting Android 15 2024-07-15 16:12:05 +02:00
Sylvain Berfini
c68db48de9 Speed up a bit conversation update security level 2024-07-15 16:12:05 +02:00
Sylvain Berfini
e80c0e6068 Added back IPv6 network setting 2024-07-15 16:12:05 +02:00
Sylvain Berfini
b3464433ed Updated CHANGELOG 2024-07-15 16:12:05 +02:00
Sylvain Berfini
5cd0a741ab Forgot to break loop + fixed issue in log 2024-07-15 16:12:05 +02:00
Sylvain Berfini
60a3752fe8 Factorized code 2024-07-15 16:12:05 +02:00
Sylvain Berfini
217f116324 Hidden separator in accounts list cells 2024-07-15 16:12:05 +02:00
Sylvain Berfini
1b644741c6 Bumped gradle to 8.7.0 and AGP to 8.5.0 2024-07-15 16:12:05 +02:00
Sylvain Berfini
c7586feebc Fixed group chat room security level indicator over avatar 2024-07-15 16:12:05 +02:00
Sylvain Berfini
5b80833f30 Reworked chat message forward UI/UX 2024-07-15 16:12:05 +02:00
Sylvain Berfini
a858cffc82 Prevent crash if LDAP server URL wasn't filled 2024-07-15 16:12:05 +02:00
Sylvain Berfini
974e0cffc6 Started CHANGELOG 2024-07-15 16:12:03 +02:00
Sylvain Berfini
9f36ec950f Hidden main color selector 2024-07-15 16:10:44 +02:00
Sylvain Berfini
791e209d62 x86 & x86_64 ABIs are now removed 2024-07-15 16:10:44 +02:00
Sylvain Berfini
cb589b95c8 Bumped agp to 8.4.2 2024-07-15 16:10:44 +02:00
Sylvain Berfini
5e727b4e42 Fixed conversation opened upon rotating the device after clicking on message notification 2024-07-15 16:10:44 +02:00
Sylvain Berfini
058a88424c Token to read in ZRTP SAS validation is now in bold 2024-07-15 16:10:44 +02:00
Sylvain Berfini
3ccf4dc3a6 Check if SIP addresses fields are empty before parsing them 2024-07-15 16:10:44 +02:00
Sylvain Berfini
b7996b9e82 Added missing URI handler schemes 2024-07-15 16:10:44 +02:00
Sylvain Berfini
274ed49f16 Update ZRTP related code to use newly added APIs 2024-07-15 16:10:44 +02:00
Sylvain Berfini
fb75ea344c Fixed reply display name when it's one of our messages that has been replied to 2024-07-15 16:10:44 +02:00
Sylvain Berfini
483e88a02d Force navigation bar to use dark mode in Call activity 2024-07-15 16:10:44 +02:00
Sylvain Berfini
ef9339e912 Renamed & moved around some strings 2024-07-15 16:10:44 +02:00
Sylvain Berfini
896b621545 Sort delivery status lists by timestamp 2024-07-15 16:10:44 +02:00
Sylvain Berfini
12e7041c57 Moved some settings around, removed ADB logcat toggle 2024-07-15 16:10:44 +02:00
Sylvain Berfini
01f6ac29e9 Adding clear input when long pressing backspace button in numpad + playing DTMF tone if enabled at OS level 2024-07-15 16:10:44 +02:00
Sylvain Berfini
a71ba2096b Fixed auto start call recording setting 2024-07-15 16:10:44 +02:00
Sylvain Berfini
ee4e332330 Removed route audio to bluetooth setting, already done by TelecomManager 2024-07-15 16:10:44 +02:00
Sylvain Berfini
f9f25b2b15 Removed auto-export media, won't work with auto download 2024-07-15 16:10:44 +02:00
Sylvain Berfini
87bfe5b6c4 Renaming a few things to make it better understandable 2024-07-15 16:10:44 +02:00
Sylvain Berfini
88c230f136 Removed secureMode/interopMode related code for now, will do it again the day it will be available in SDK + minor improvements 2024-07-15 16:10:44 +02:00
Sylvain Berfini
cfe7a8ed38 Added confirmation dialog before clearing conversation history 2024-07-15 16:10:44 +02:00
Sylvain Berfini
a3489f4064 Fixed text file sharing (including linphone config) 2024-07-15 16:10:44 +02:00
Sylvain Berfini
ab9aedf5ee Fixed file viewer header colors in dark mode 2024-07-15 16:10:44 +02:00
Sylvain Berfini
aab5863b57 Reworked Core startup & configuration 2024-07-15 16:10:44 +02:00
Sylvain Berfini
0238de5b3b Added log regarding Core's video policy 2024-07-15 16:10:44 +02:00
Sylvain Berfini
b8e2541cda Updated dependencies 2024-07-15 16:10:44 +02:00
Sylvain Berfini
87153679a3 No longer need to remove reply action from message notification 2024-07-15 16:10:44 +02:00
Sylvain Berfini
91df15c022 Auto enable keep-alive foreground service when configuring an account that requires it 2024-07-15 16:10:44 +02:00
Sylvain Berfini
8f4c5bdc61 Getting rid of dataSync foreground service type for keeping app alive in background as it won't work more than 6h a day starting Android 15 2024-07-15 16:10:44 +02:00
Sylvain Berfini
84b39df26c Removed unused code + updated account settings layout 2024-07-15 16:10:44 +02:00
Sylvain Berfini
1741ed5fe5 Fixed clicking message notification not going into conversation 2024-07-15 16:10:44 +02:00
Sylvain Berfini
56d8e64762 Fixed being stuck in contacts if permission wasn't granted 2024-07-15 16:10:44 +02:00
Sylvain Berfini
78f1a1e645 Updated recordings list & added recording player (audio & video) 2024-07-15 16:10:44 +02:00
Sylvain Berfini
37786a0b83 Added copy SIP address in account profile 2024-07-15 16:10:44 +02:00
Sylvain Berfini
824b225549 No longer needed to filter out deprecated devices 2024-07-15 16:10:44 +02:00
Sylvain Berfini
4c127344c1 Forgot file viewer inset padding + allow SIP address in account profile to take two lines 2024-07-15 16:10:44 +02:00
Sylvain Berfini
61c1079e7c Revert "Targetting & compiling for Android 15", it prevents from installing on Android older than 15
This reverts commit 21a96a403406752e9f5793fc696ce61673a042ab.
2024-07-15 16:10:44 +02:00
Sylvain Berfini
dcbd978184 Keep shortedges instead of always as the later was only added in Android 11 2024-07-15 16:10:44 +02:00
Sylvain Berfini
fa6d93076c Targetting & compiling for Android 15 2024-07-15 16:10:44 +02:00
Sylvain Berfini
a95c6fb287 Android 15 will force edge-to-edge, starting supporting it now 2024-07-15 16:10:44 +02:00
Sylvain Berfini
e039a562fe Fixed chronometer not hidden in fullscreen 2024-07-15 16:10:44 +02:00
Sylvain Berfini
69d149a284 Trying to prevent sliding pane child to open when app starts after it was killed while in background with sliding pane child opened 2024-07-15 16:10:44 +02:00
Sylvain Berfini
6d099a3075 Enable proximity sensor for outgoing calls if video is disabled 2024-07-15 16:10:44 +02:00
Sylvain Berfini
eea978d8e8 Improved opening meeting with a lot of participants fluidity 2024-07-15 16:10:44 +02:00
Sylvain Berfini
46a6751df6 Fixed meetings participants not aligned top in list 2024-07-15 16:10:44 +02:00
Sylvain Berfini
1594252aeb Updated icon & text for in-call media encryption label for SRTP 2024-07-15 16:10:44 +02:00
Sylvain Berfini
ec207af81d Added filter for recordings + legacy recording files 2024-07-15 16:10:44 +02:00
Sylvain Berfini
cda23c44c0 Added a logger for Android Auto connection state + do not use bluetooth sound card as playback device for voice recordings when connected to Auto 2024-07-15 16:10:44 +02:00
Sylvain Berfini
144fc5d728 Fixed back stack with conversation shortcuts on launcher icon 2024-07-15 16:10:44 +02:00
Sylvain Berfini
a424f05ab9 Fixed meeting participants list not showing all of them 2024-07-15 16:10:44 +02:00
Sylvain Berfini
481e8db0aa Keep phone number label(s) if any after editing a contact 2024-07-15 16:10:44 +02:00
Sylvain Berfini
f6e6914656 Leave CardDAV config fragment when sync is succesful 2024-07-15 16:10:44 +02:00
Sylvain Berfini
75ce3a9c05 Fixed toast color 2024-07-15 16:10:44 +02:00
Sylvain Berfini
e83afdf436 Update meeting details after successfull edit 2024-07-15 16:10:44 +02:00
Sylvain Berfini
5d58b2a0fd Ask contacts permission in fragment's onResume() if needed 2024-07-15 16:10:44 +02:00
Sylvain Berfini
133f533b87 Fixed back stack when sharing logs using conversation 2024-07-15 16:10:44 +02:00
Sylvain Berfini
c927a83aa5 Migrated buildconfig 2024-07-15 16:10:44 +02:00
Sylvain Berfini
3a2757d962 Alert user when player can't open a voice recording 2024-07-15 16:10:44 +02:00
Sylvain Berfini
2e8b258b90 Updated android gradle plugin version 2024-07-15 16:10:44 +02:00
Sylvain Berfini
37df7d83e3 Updated crashlytics & firebase BoM 2024-07-15 16:10:44 +02:00
Sylvain Berfini
dddfdd8b78 Updated settings.gradle.kts to only have one maven repository for linphone, depending on if local exists or not 2024-07-15 16:10:44 +02:00
Sylvain Berfini
76716503e9 Added Tunnel settings 2024-07-15 16:10:44 +02:00
Sylvain Berfini
26e1332421 Added share / download long press menu items for call recordings 2024-07-15 16:10:44 +02:00
Sylvain Berfini
6cfec04424 Added MWI urgent messages count to drawer 2024-07-15 16:10:44 +02:00
Sylvain Berfini
bf64b496c9 Added audio & video codecs settings in advanced parameters 2024-07-15 16:10:44 +02:00
Sylvain Berfini
4f6c5c7f48 Added audio device picker in advanced settings 2024-07-15 16:10:44 +02:00
Sylvain Berfini
d48f7697df Added MWI URI account setting + added missing hints into text based settings 2024-07-15 16:10:44 +02:00
Sylvain Berfini
99e771898a Prevents attaching more than 12 files to a same message 2024-07-15 16:10:44 +02:00
Sylvain Berfini
7cf51f51a1 Added explanation bottom sheet for unsafe conversations instead of going into account profile mode fragment that was recently removed 2024-07-15 16:10:44 +02:00
Sylvain Berfini
fc8ac5fc56 Only go to choose secure/interop mode after successfully connecting first sip.linphone.org account 2024-07-15 16:10:44 +02:00
Sylvain Berfini
7cff514c3a Hidden change mode area & child fragment in account profile, moved IM encryption mandatory to account settings 2024-07-15 16:10:44 +02:00
Sylvain Berfini
3060af3940 Fixed size issue for chat media grid item 2024-07-15 16:10:44 +02:00
Sylvain Berfini
458f817142 Fixed missed call(s) indicator not visible or not hidden in drawer menu 2024-07-15 16:10:44 +02:00
Sylvain Berfini
701117474e Fixed main activity rotation issue + back stack not empty when clicking chat message notification 2024-07-15 16:10:44 +02:00
Sylvain Berfini
e93e6ca5cc Renamed recordings fragment 2024-07-15 16:10:44 +02:00
Sylvain Berfini
f8d60c1284 Moved push notif account toggle from profile to settings 2024-07-15 16:10:44 +02:00
Sylvain Berfini
01d721d477 Added countdowns in chat bubbles for ephemeral messages 2024-07-15 16:10:43 +02:00
Sylvain Berfini
6ecc0839ea Added empty recordings list image & label 2024-07-15 16:10:43 +02:00
Sylvain Berfini
1d5e496f3f Fix for recording player status when starting another one 2024-07-15 16:10:43 +02:00
Sylvain Berfini
3adbd83259 Fixed some icons color 2024-07-15 16:10:43 +02:00
Sylvain Berfini
376af91e88 Reworked file viewer interface, improved video/audio player 2024-07-15 16:10:43 +02:00
Sylvain Berfini
26e30c6060 Improved startup reactivity 2024-07-15 16:10:43 +02:00
Sylvain Berfini
f6545f5641 Reworked Groovy gradle files to Kotlin 2024-07-15 16:10:43 +02:00
Sylvain Berfini
31580e6291 Reworked recordings player 2024-07-15 16:10:43 +02:00
Sylvain Berfini
4f848b182a Updated pick number or address title 2024-07-15 16:10:43 +02:00
Sylvain Berfini
d5f43323a2 Increase play/pause button size in recordings list 2024-07-15 16:10:43 +02:00
Sylvain Berfini
f90b518f43 Added a setting to disable UI secure mode preventing screenshots & screen recording 2024-07-15 16:10:43 +02:00
Sylvain Berfini
8f1eeebd66 Fixed meetings briefly visible sometimes 2024-07-15 16:10:43 +02:00
Sylvain Berfini
9a97a45448 Do not start welcome or assistant acitvities if remote provisioning URI is given in intent data 2024-07-15 16:10:43 +02:00
Sylvain Berfini
1ea32e7544 Added simple call recording player 2024-07-15 16:10:43 +02:00
Sylvain Berfini
80da408930 Downgraded material version to fix contact devices progress bar 2024-07-15 16:10:43 +02:00
Sylvain Berfini
8303b356da Added offline_access scope to SSO builder so SDK can refresh token 2024-07-15 16:10:43 +02:00
Sylvain Berfini
a557875ce8 Started call recordings list 2024-07-15 16:10:43 +02:00
Sylvain Berfini
4d561a4635 Updated SSO to add client ID & token endpoint URL to auth info 2024-07-15 16:10:43 +02:00
Sylvain Berfini
fa7d4bc267 Updated dependencies 2024-07-15 16:10:43 +02:00
Sylvain Berfini
876029cbc9 Added back proximity sensor during active audio calls while using the earpiece 2024-07-15 16:10:43 +02:00
Sylvain Berfini
e9f7e4dcf5 Added missing cap sentences flag to display name field in third party account login 2024-07-15 16:10:43 +02:00
Sylvain Berfini
82071985bc Added back secure flag to prevent screenshots 2024-07-15 16:10:43 +02:00
Sylvain Berfini
10b7545807 Using OpenID library fetchFromIssuer instead of handling well-known manually 2024-07-15 16:10:43 +02:00
Sylvain Berfini
ccd53b74db Moved file & media viewer in separated activities 2024-07-15 16:10:43 +02:00
Sylvain Berfini
eb0748df7f Also close drawer menu when clicking on brand icon 2024-07-15 16:10:43 +02:00
Sylvain Berfini
ecad8fbdce Updated third party SIP account login to allow for SIP identity with a different domain for proxy + bearer auth 2024-07-15 16:10:43 +02:00
Sylvain Berfini
655cc8c291 Conversation performances improvements 2024-07-15 16:10:43 +02:00
Sylvain Berfini
889b98db1e Refactored toasts 2024-07-15 16:10:43 +02:00
Sylvain Berfini
465201010d Added missing toasts 2024-07-15 16:10:43 +02:00
Sylvain Berfini
b3ef701661 Added back .debug package name suffix for debug builds (configurable) 2024-07-15 16:10:43 +02:00
Sylvain Berfini
d062910133 Added dialog asking user whether to open or export file that app can't display 2024-07-15 16:10:43 +02:00
Sylvain Berfini
405596d291 Added multi-accounts notifications top bar 2024-07-15 16:10:43 +02:00
Sylvain Berfini
63b06ed1fa Improved audio file duration label color 2024-07-15 16:10:43 +02:00
Sylvain Berfini
bb844739c1 Display audio files duration as well 2024-07-15 16:10:43 +02:00
Sylvain Berfini
4fe0487eaf Fixed display issue in media grid 2024-07-15 16:10:43 +02:00
Sylvain Berfini
121f400732 Keep displaying contact's trust level even if e2e encryption isn't mandatory 2024-07-15 16:10:43 +02:00
Sylvain Berfini
1a5bc838b9 Fixed focus issue when entering a conversation after filtering conversations list 2024-07-15 16:10:43 +02:00
Sylvain Berfini
552e158979 Fixed expiration time in bearer tokens 2024-07-15 16:10:43 +02:00
Sylvain Berfini
d8f0338f7c Improved media grid, making sub-sections by months 2024-07-15 16:10:43 +02:00
Sylvain Berfini
54a775e0fd Focus subject field of create conversation/group call dialog 2024-07-15 16:10:43 +02:00
Sylvain Berfini
9c7739cf6c Using real SDK API to know if account has configured IM encryption as mandatory or not 2024-07-15 16:10:43 +02:00
Sylvain Berfini
22254b7846 Added very simple media player for audio files 2024-07-15 16:10:43 +02:00
Sylvain Berfini
fde0ca60a1 Display audio files as well as media & images 2024-07-15 16:10:43 +02:00
Sylvain Berfini
faf20eb369 Fixed contact discard changes dialog showing up if no changes were made 2024-07-15 16:10:43 +02:00
Sylvain Berfini
68f2535072 Added confirmation dialog before removing contact from list 2024-07-15 16:10:43 +02:00
Sylvain Berfini
9df97d7594 Added splashscreen branding image 2024-07-15 16:10:43 +02:00
Sylvain Berfini
efbcfee316 Layouts cleanup 2024-07-15 16:10:43 +02:00
Sylvain Berfini
c9c362d570 Fixed scheduled meetings duration 2024-07-15 16:10:43 +02:00
Sylvain Berfini
65a3d0520f Added missing content descriptions 2024-07-15 16:10:43 +02:00
Sylvain Berfini
86beb60507 Fixed hardcoded french text 2024-07-15 16:10:43 +02:00
Sylvain Berfini
78052cae12 Code cleanup, removed unused resources, fixed some colors 2024-07-15 16:10:43 +02:00
Sylvain Berfini
65595a1d51 Updated GMS & Kotlin versions 2024-07-15 16:10:43 +02:00
Sylvain Berfini
2b315cd0e4 Fixed issues related to multi call when one of them is a conference 2024-07-15 16:10:43 +02:00
Sylvain Berfini
b880578f99 Improved show video stats in call condition 2024-07-15 16:10:43 +02:00
Sylvain Berfini
3343b728fa Added no participants selected placeholder 2024-07-15 16:10:43 +02:00
Sylvain Berfini
3b588f467a Allow to cancel meeting removal by clicking outside of dialog 2024-07-15 16:10:43 +02:00
Sylvain Berfini
fec0766501 Improved SSO error log + added retry using well-known 2024-07-15 16:10:43 +02:00
Sylvain Berfini
ffa9a909a9 Bumped dependencies 2024-07-15 16:10:43 +02:00
Sylvain Berfini
507fb8a3ce Reworked auth requested callback & SSO activity to handle Bearer authentication requests 2024-07-15 16:10:43 +02:00
Sylvain Berfini
ebf9fa9145 Fixed waiting room joining screen when rotating device 2024-07-15 16:10:43 +02:00
Sylvain Berfini
9ca6978b7c Fixed issue when trying to open a file received by chat if the in-app viewer doesn't support it 2024-07-15 16:10:43 +02:00
Sylvain Berfini
d55c60cd98 Fixed display issue when switching between accounts if one of them isn't registered 2024-07-15 16:10:43 +02:00
Sylvain Berfini
a9c28aa1aa Fixed status bar color in dark mode when using a non-default theme 2024-07-15 16:10:43 +02:00
Sylvain Berfini
2cef97bce1 Improved a bit conversation participants mass removal 2024-07-15 16:10:43 +02:00
Sylvain Berfini
ed541bf5c0 Hide horizontal scroll bar in participants selection fragments 2024-07-15 16:10:43 +02:00
Sylvain Berfini
107388584b Also updated conversations participants selection to handle removal from add participant(s) fragment 2024-07-15 16:10:43 +02:00
Sylvain Berfini
1719a57cdc Updated meetings participants picker 2024-07-15 16:10:43 +02:00
Sylvain Berfini
b4d25b0e6e Moved click to add participants label in meeting edit fragment to top of participants list + prepend newly added participants in picker 2024-07-15 16:10:43 +02:00
Sylvain Berfini
b4baddcc5b Added shortcut to account profile mode fragment from plain text conversation 2024-07-15 16:10:43 +02:00
Sylvain Berfini
cf7dbb7f61 Added fake implem for Account.isEndToEndEncryptionMandatory() + possibility to change mode 2024-07-15 16:10:43 +02:00
Sylvain Berfini
7633af198a Simplified themes 2024-07-15 16:10:43 +02:00
Sylvain Berfini
20f4a072c4 Also tint app icon in drawer menu 2024-07-15 16:10:43 +02:00
Sylvain Berfini
0c7a6bb3b3 Added missing tint on mountains drawable 2024-07-15 16:10:43 +02:00
Sylvain Berfini
ca475c0ab8 Enabled FEC by default + added setting 2024-07-15 16:10:42 +02:00
Sylvain Berfini
7e06c5d85a Added error toast with reason when call ends in error 2024-07-15 16:10:42 +02:00
Sylvain Berfini
0239d3a3a6 Fixed switch to grid layout in conference if there is exactly 6 participants 2024-07-15 16:10:42 +02:00
Sylvain Berfini
0dbd403d5b Check if current fragment matches before going back 2024-07-15 16:10:42 +02:00
Sylvain Berfini
39fba60066 Fixed groip chat room shortcuts generated avatar not using initials 2024-07-15 16:10:42 +02:00
Sylvain Berfini
8e2fc8b6cd Improved account profile if push notifications aren't available on the device (or not configured in app) 2024-07-15 16:10:42 +02:00
Sylvain Berfini
74ad52e0f5 Also fixed voice recording in progress gradient color when a theme is being used & removed orange_main_400 that wasn't used 2024-07-15 16:10:42 +02:00
Sylvain Berfini
42e2a1c040 Updated mountains drawable to properly handle main color change 2024-07-15 16:10:42 +02:00
Sylvain Berfini
f532214e1a Add already selected participants to list when adding new participants 2024-07-15 16:10:42 +02:00
Sylvain Berfini
e9f0bed2d2 Close search bar when clicking on clear filter button if filter is empty 2024-07-15 16:10:42 +02:00
Sylvain Berfini
00b92a61b4 Improved app fluidity 2024-07-15 16:10:42 +02:00
Sylvain Berfini
41de644945 Trying to prevent getting stuck in permissions fragment 2024-07-15 16:10:42 +02:00
Sylvain Berfini
fb7c3a3cdc Reload accounts when applying remote provisioning 2024-07-15 16:10:42 +02:00
Sylvain Berfini
9daa433c44 Display week info above meeting in list 2024-07-15 16:10:42 +02:00
Sylvain Berfini
177eb186a5 Improved meetings list design 2024-07-15 16:10:42 +02:00
Sylvain Berfini
d848622ace Removed avatar generation for conversations/meetings depending on members avatars 2024-07-15 16:10:42 +02:00
Sylvain Berfini
81af7a8bc0 Update new conversation / meeting icons 2024-07-15 16:10:42 +02:00
Sylvain Berfini
6856a399c5 Updated meeting icon, now using video_conference from phosphoricons 2024-07-15 16:10:42 +02:00
Sylvain Berfini
84f7af8d13 Disable change audio output device until call is in Ringing state 2024-07-15 16:10:42 +02:00
Sylvain Berfini
2070f8fb08 Fixed dialog theme status bar color + added more colors (for fun) 2024-07-15 16:10:42 +02:00
Sylvain Berfini
fbf2d39640 Set front facing camera as default one each time last calls end 2024-07-15 16:10:42 +02:00
Sylvain Berfini
6b0bae9c3d Increased trust/unsafe avatar overlay size for small/medium avatars so they are more visible 2024-07-15 16:10:42 +02:00
Sylvain Berfini
bd197bd219 Fixed issue with full screen 2024-07-15 16:10:42 +02:00
Sylvain Berfini
5300cf698d Disable full screen mode when remote device stops sending it's video 2024-07-15 16:10:42 +02:00
Sylvain Berfini
f048b895b5 Added main color theme selector in settings 2024-07-15 16:10:42 +02:00
Sylvain Berfini
fdafcfd7a4 Added dialog to confirm removing a participant from conference 2024-07-15 16:10:42 +02:00
Sylvain Berfini
79961739e0 Improved managed own calls permission request 2024-07-15 16:10:42 +02:00
Sylvain Berfini
33865b469c Prepared listener when clicking on disabled conversation warning 2024-07-15 16:10:42 +02:00
Sylvain Berfini
502c7f9fc1 Fixed crash in NotificationBroadcastReceiver if coreContext isn't created when intent is received 2024-07-15 16:10:42 +02:00
Sylvain Berfini
9c855ef923 Improved Linphone login adding automatically default domain to username if not entered by user 2024-07-15 16:10:42 +02:00
Sylvain Berfini
6ceee7fdb7 Made welcome pages scrollable, required for small screens 2024-07-15 16:10:42 +02:00
Sylvain Berfini
498b8435bf Hide contact's devices & trust for third party accounts 2024-07-15 16:10:42 +02:00
Sylvain Berfini
1578e76700 Refactored code a bit to make it simplier 2024-07-15 16:10:42 +02:00
Sylvain Berfini
fbb59cbc2a When upgrading from previous version, do not show AssitantLanding page after WelcomeActivity and/or PermissionsFragment 2024-07-15 16:10:42 +02:00
Sylvain Berfini
23c3a63aed Improved toast layout for small screen devices 2024-07-15 16:10:42 +02:00
Sylvain Berfini
cacaf29771 Should fix black avatar for chat rooms & conferences if there is a picture set for a contact but that file can't be read for some reason 2024-07-15 16:10:42 +02:00
Sylvain Berfini
559397d420 Added support for JSON file in plain text file viewer 2024-07-15 16:10:42 +02:00
Sylvain Berfini
a64db777d9 Various fixes for crash reported on Crashlytics 2024-07-15 16:10:42 +02:00
Sylvain Berfini
aa36129053 Fixed mute microphone in waiting room not applied once conference has been joined 2024-07-15 16:10:42 +02:00
Sylvain Berfini
1aeb917d62 Added download & apply button next to remote provisioning URL advanced settings 2024-07-15 16:10:42 +02:00
Sylvain Berfini
486f905d65 Added data sync keep alive service for third party accounts without push notifications 2024-07-15 16:10:42 +02:00
Sylvain Berfini
7f1dc95cfc Fluidify navigation if SDK is under burst 2024-07-15 16:10:42 +02:00
Sylvain Berfini
ace9e02133 Added clear cache files menu in help 2024-07-15 16:10:42 +02:00
Sylvain Berfini
770e816468 Hide file size in chat bubble when 0 (for outgoing messages) 2024-07-15 16:10:42 +02:00
Sylvain Berfini
bbe26ec35b Renamed a few classes related to call logs for better understanding 2024-07-15 16:10:42 +02:00
Sylvain Berfini
c002ea3205 Added missing license header on some files 2024-07-15 16:10:42 +02:00
Sylvain Berfini
0fe03e2eec Removed SSO fragment, only keep activity 2024-07-15 16:10:42 +02:00
Sylvain Berfini
5aa949b42c Added account setting to enable/disable CPIM in basic chat rooms when in interop mode 2024-07-15 16:10:42 +02:00
Sylvain Berfini
11cab8c4a4 Changes in gradle dependencies 2024-07-15 16:10:42 +02:00
Sylvain Berfini
7c94bd8b67 Layout improvements for phones with small width such as Samsung S22 2024-07-15 16:10:42 +02:00
Sylvain Berfini
4f25dfb33c Fixed participants list in conversation info scrolling 2024-07-15 16:10:42 +02:00
Sylvain Berfini
b96e5e8121 Using onlyDisplaySipUriUsername preference 2024-07-15 16:10:42 +02:00
Sylvain Berfini
2d1479a64f Fixed camera switch button not visible when alone in a conf 2024-07-15 16:10:42 +02:00
Sylvain Berfini
a13b46bd2c Bumped dependencies 2024-07-15 16:10:42 +02:00
Sylvain Berfini
c4358f20f5 Fixed doubled stack conversations list fragment causing file sharing from outside not working if Linphone opened and already in conversations list 2024-07-15 16:10:42 +02:00
Sylvain Berfini
fd7700a819 Another attempt to fix duplicated messages sometimes 2024-07-15 16:10:42 +02:00
Sylvain Berfini
48ec21def4 Fixed missing end day date picker when all day meeting toggle is ON 2024-07-15 16:10:42 +02:00
Sylvain Berfini
4ce7c90577 Updated style of 'do SAS validation again' in-call button 2024-07-15 16:10:42 +02:00
Sylvain Berfini
896ede0b88 Fixed conf stuck in full screen mode when last participant leaves 2024-07-15 16:10:42 +02:00
Sylvain Berfini
ed05b648d1 Updated active speaker portrait layout + added landscape layout 2024-07-15 16:10:42 +02:00
Sylvain Berfini
b6001544f1 Added back sip-linphone & sips-linphone URI handlers + added linphone-sso URI handler & activity + factorized showToast methods 2024-07-15 16:10:42 +02:00
Sylvain Berfini
7607607857 Fixed video direction when toggling camera during call 2024-07-15 16:10:42 +02:00
Sylvain Berfini
0c261ca4cb Improved opened conversation view when a lot of unread messages are in it 2024-07-15 16:10:42 +02:00
Sylvain Berfini
4eb11a05bc Another fix for in-call navigation 2024-07-15 16:10:42 +02:00
Sylvain Berfini
e616582162 Started advanced settings, currently only contains remote provisioning URI 2024-07-15 16:10:42 +02:00
Sylvain Berfini
7b13bb3bd2 Fixed toast icon for deletion actions 2024-07-15 16:10:42 +02:00
Sylvain Berfini
b16ca7325e Fixed crash when merging calls into conference 2024-07-15 16:10:42 +02:00
Sylvain Berfini
81bb7156dc Fixed merge calls into conference dialog label 2024-07-15 16:10:42 +02:00
Sylvain Berfini
54818c51b4 Prevents duplicated message when entering a conversation while SDK is still doing aggregation 2024-07-15 16:10:42 +02:00
Sylvain Berfini
9e3c679665 Fixed permissions layout issue in landscape + assistant landing page on narrow screen 2024-07-15 16:10:42 +02:00
Sylvain Berfini
637d0b9cfb Fixed weird video preview shape while in PiP 2024-07-15 16:10:42 +02:00
Sylvain Berfini
61fe57628f Improved PiP 2024-07-15 16:10:42 +02:00
Sylvain Berfini
8c7c4bee0d Update call foreground service types when disabling/enabling video 2024-07-15 16:10:42 +02:00
Sylvain Berfini
7e0208f8e8 Reversed media/documents list order to have most recents ones at the top 2024-07-15 16:10:42 +02:00
Sylvain Berfini
680c6877e4 Do not start audio calls with video enabled but in inactive direction 2024-07-15 16:10:42 +02:00
Sylvain Berfini
16fa960a0b Do not notify received reactions for messages we didn't sent + no longer repeat person reacting name in notification body 2024-07-15 16:10:42 +02:00
Sylvain Berfini
c0b0ef66ff Trying to fix lost remote video upon rotation 2024-07-15 16:10:42 +02:00
Sylvain Berfini
3e845b32b6 Improved meeting room fragment, added cancel button 2024-07-15 16:10:42 +02:00
Sylvain Berfini
b0a05b5905 Updated gradle, fixed configuration migration 2024-07-15 16:10:42 +02:00
Sylvain Berfini
afc2017e1e Fixed video setting hidden when disabled 2024-07-15 16:10:42 +02:00
Sylvain Berfini
5a37f15bc7 Fixed issues with incoming call notification not being dismissed in some cases + fixed toast in case of remote provisioning issue 2024-07-15 16:10:42 +02:00
Sylvain Berfini
6e97466a86 Factorized code for call.terminate()/call.decline() 2024-07-15 16:10:42 +02:00
Sylvain Berfini
0d96010865 Started merge calls into conference 2024-07-15 16:10:42 +02:00
Sylvain Berfini
fea42aba3b Display our own video (if enabled) while waiting for other participants to join the conference 2024-07-15 16:10:42 +02:00
Sylvain Berfini
065cdfa8c1 Fixed video preview moving depending on remote end video being sent or not 2024-07-15 16:10:42 +02:00
Sylvain Berfini
16d15242c7 Improved connection to meeting waiting screen 2024-07-15 16:10:42 +02:00
Sylvain Berfini
4d8b74ee41 Fixed participant added/removed toast 2024-07-15 16:10:41 +02:00
Sylvain Berfini
f5bdaf85fd Improved contacts display, fixed issue with LDAP results 2024-07-15 16:10:41 +02:00
Sylvain Berfini
9c1b9b2939 Using new APIs to be able to make asymetrical video calls 2024-07-15 16:10:41 +02:00
Sylvain Berfini
d6ea531cea Moved some code around & added invite participant into conf 2024-07-15 16:10:41 +02:00
Sylvain Berfini
d19f08cf86 Started to add chat during call 2024-07-15 16:10:41 +02:00
Sylvain Berfini
e805fbc7f3 Prevent blue lining & trusted badge on conference avatar 2024-07-15 16:10:41 +02:00
Sylvain Berfini
0db1754603 Change contacts list filter function depending on whether the default account is a Linphone account in secure mode or something else 2024-07-15 16:10:41 +02:00
Sylvain Berfini
f70ab87952 Started admin view of conference participants list 2024-07-15 16:10:41 +02:00
Sylvain Berfini
561216320f Fixed empty bubble when shared file is considered as plain/text 2024-07-15 16:10:41 +02:00
Sylvain Berfini
1ae85611bf Fixed margin around images with large ratio between w/h in chat bubbles 2024-07-15 16:10:41 +02:00
Sylvain Berfini
cab66e844b Added missing cross to close number/address picker dialog 2024-07-15 16:10:41 +02:00
Sylvain Berfini
d693cfe58d Removed ringtone picker setting 2024-07-15 16:10:41 +02:00
Sylvain Berfini
86387ef5b8 Improved multi files chat bubble 2024-07-15 16:10:41 +02:00
Sylvain Berfini
0707e60c26 Reworked notifications channel, using incoming call one for playing ringtone instead of SDK 2024-07-15 16:10:41 +02:00
Sylvain Berfini
44457665ef Prevent release build from failing due to Android linter 2024-07-15 16:10:41 +02:00
Sylvain Berfini
19cee069ec Updated icons 2024-07-15 16:10:41 +02:00
Sylvain Berfini
9416305f61 Added locus ID management in conversation 2024-07-15 16:10:41 +02:00
Sylvain Berfini
7b115caf61 Added protobuf javalite dependency to parse native crash tombstone 2024-07-15 16:10:41 +02:00
Sylvain Berfini
7076acc540 Improved a few things related to conversation 2024-07-15 16:10:41 +02:00
Sylvain Berfini
8a2e2c074b Updated gradle version 2024-07-15 16:10:41 +02:00
Sylvain Berfini
e438617241 Improved multiple files upload/sending chat bubble 2024-07-15 16:10:41 +02:00
Sylvain Berfini
4098827253 Fixed square image in contact editor 2024-07-15 16:10:41 +02:00
Sylvain Berfini
945fd709d4 Fixed media list when VFS is enabled + removed debug logs and trying something else instead 2024-07-15 16:10:41 +02:00
Sylvain Berfini
30927ac6db Should prevent going to the wrong conversation when multiple message notifications are still visible 2024-07-15 16:10:41 +02:00
Sylvain Berfini
dd96cac3d0 Added temporary logs to help debug duplicated chat messages sometimes 2024-07-15 16:10:41 +02:00
Sylvain Berfini
b0f5141e7d Fixed crash when loading more data in conversation 2024-07-15 16:10:41 +02:00
Sylvain Berfini
578b372335 Fixed used of some interpretUrl method calls 2024-07-15 16:10:41 +02:00
Sylvain Berfini
a871fb971b Updated conference invitation chat message layout to add label/icon when meeting is updated/cancelled 2024-07-15 16:10:41 +02:00
Sylvain Berfini
b41bc3bb7d Updated message description depending on conference info state (cancelled, updated, etc...) 2024-07-15 16:10:41 +02:00
Sylvain Berfini
af8071bf0e Update chat rooms list when a message is deleted in a conversation 2024-07-15 16:10:41 +02:00
Sylvain Berfini
fcf2ffa39c Added dialog asking whether to cancel the meeting or not when deleting it 2024-07-15 16:10:41 +02:00
Sylvain Berfini
2505aad1e8 Prevent smiley/offline presence being briefly visible when switching conversation on tablet 2024-07-15 16:10:41 +02:00
Sylvain Berfini
5da4f748a3 Fixed operation in progress dialog background color transparency 2024-07-15 16:10:41 +02:00
Sylvain Berfini
63138e818c Added operation in progress dialog while upload logs 2024-07-15 16:10:41 +02:00
Sylvain Berfini
b3c31d14ad Use default string from resources if a contact's device has no name, updated TODOs & FIXMEs comments 2024-07-15 16:10:41 +02:00
Sylvain Berfini
57b6c5daa2 Disable recordings in call & conference if corePreferences related setting is disabled 2024-07-15 16:10:41 +02:00
Sylvain Berfini
8ec7542a60 Store and use 'do not display anymore' option of zrtp trust call dialog 2024-07-15 16:10:41 +02:00
Sylvain Berfini
d64f1e033b Improved some spinners display 2024-07-15 16:10:41 +02:00
Sylvain Berfini
6b9ca15dff Fixed dialplan prefix issue in account creator 2024-07-15 16:10:41 +02:00
Sylvain Berfini
2ca3d152d1 Restored initial login form on assistant landing page 2024-07-15 16:10:41 +02:00
Sylvain Berfini
5ab0b32705 Added log in new Message Waiting Indicator (MWI) account callback 2024-07-15 16:10:41 +02:00
Sylvain Berfini
5f82cf9066 Fixed scolling in contact editor while in landscape 2024-07-15 16:10:41 +02:00
Sylvain Berfini
46570a4152 Fixed ephemeral message conversation menu item not working 2024-07-15 16:10:41 +02:00
Sylvain Berfini
34c3dff137 Do ZRTP SAS validation again button now working 2024-07-15 16:10:41 +02:00
Sylvain Berfini
91e0cb5838 Fixes & improvements for incoming group calls 2024-07-15 16:10:41 +02:00
Sylvain Berfini
90a2c0539c Fixed conversation info toast + participants list not refreshed + events not appearing in messages list when going back 2024-07-15 16:10:41 +02:00
Sylvain Berfini
7f24902fc7 Fixed new call/chat search bar length in landscape 2024-07-15 16:10:41 +02:00
Sylvain Berfini
81f0a9515f Hide some parts of the UI depending on Core or account configuration 2024-07-15 16:10:41 +02:00
Sylvain Berfini
7d7b037741 Added some customization settings 2024-07-15 16:10:41 +02:00
Sylvain Berfini
37af11d3e1 Improved VFS class code a bit 2024-07-15 16:10:41 +02:00
Sylvain Berfini
c35025aedb Added save on disk for plain text file, reworked share button to use native sharing 2024-07-15 16:10:41 +02:00
Sylvain Berfini
70e25b7792 Added button in debug help fragment to display config file 2024-07-15 16:10:41 +02:00
Sylvain Berfini
d91093cf01 Added VFS & security settings section 2024-07-15 16:10:41 +02:00
Sylvain Berfini
8226f6e1b3 Factorized avatar + presence & trust badges 2024-07-15 16:10:41 +02:00
Sylvain Berfini
d023519cd8 Use linphone API to get contact's devices & trust 2024-07-15 16:10:41 +02:00
Sylvain Berfini
2de4067b03 Added missing contacts settings expand value initialization 2024-07-15 16:10:41 +02:00
Sylvain Berfini
44009cfd92 Do not notify early contacts list after first time 2024-07-15 16:10:41 +02:00
Sylvain Berfini
ddcd7d7dc1 Removed emoji compat library, only use it for picker 2024-07-15 16:10:41 +02:00
Sylvain Berfini
3e6c856ee5 Fixed build with latest SDK 2024-07-15 16:10:41 +02:00
Sylvain Berfini
29218a5311 Hide 'mode selector' in account profile for third party SIP accounts + fixed issues when switching between a linphone account and a third party one 2024-07-15 16:10:41 +02:00
Sylvain Berfini
dcca7d6952 Started CardDAV & LDAP configuration fragments 2024-07-15 16:10:41 +02:00
Sylvain Berfini
deaf9cd0db Fixed contacts list 'header' for emojis 2024-07-15 16:10:41 +02:00
Sylvain Berfini
38eeb56741 Keep synchronizing native address book even now that imported friends are stored in database 2024-07-15 16:10:41 +02:00
Sylvain Berfini
9b61700b79 Improved emoji library loading wait 2024-07-15 16:10:41 +02:00
Sylvain Berfini
c90961408f Added bundle mode account setting 2024-07-15 16:10:41 +02:00
Sylvain Berfini
0fac6150c7 Added support for screen sharing using new SDK APIs 2024-07-15 16:10:41 +02:00
Sylvain Berfini
719fa62752 Trying to wait for emoji lib to have loaded before displaying account display name, in case it contains emoji(s) 2024-07-15 16:10:41 +02:00
Sylvain Berfini
f19fbb66e7 Updated gradle 2024-07-15 16:10:41 +02:00
Sylvain Berfini
8aac439a5b Added landscape version of contact editor + fixed issue in dark mode 2024-07-15 16:10:41 +02:00
Sylvain Berfini
1d7f531f6a Added missing trust badge in meetings' participants list 2024-07-15 16:10:41 +02:00
Sylvain Berfini
eefbec5358 Fixed dark mode issues 2024-07-15 16:10:41 +02:00
Sylvain Berfini
3aaea594f4 Prevent typing message after starting voice recording in chat by disabling field & hiding keyboard 2024-07-15 16:10:41 +02:00
Sylvain Berfini
40610fd98f Trim fields in assistant 2024-07-15 16:10:41 +02:00
Sylvain Berfini
9a1ca386ca Store native contacts copy inside app 2024-07-15 16:10:41 +02:00
Sylvain Berfini
3a2d85265d Small improvements 2024-07-15 16:10:41 +02:00
Sylvain Berfini
01c69b9396 Fixed dark theme issue 2024-07-15 16:10:41 +02:00
Sylvain Berfini
d61f94c42e Renamed a few strings 2024-07-15 16:10:41 +02:00
Sylvain Berfini
70d128edab Finished French translations 2024-07-15 16:10:41 +02:00
Sylvain Berfini
a3966a72ad Fixed issues with participant video window id in confernece 2024-07-15 16:10:41 +02:00
Sylvain Berfini
5c4d73f34a Started french translation 2024-07-15 16:10:41 +02:00
Sylvain Berfini
d2b8689f48 Fixed call history list cells not refreshed when native contacts are loaded or presence is received 2024-07-15 16:10:41 +02:00
Sylvain Berfini
c9eb856f19 Improved stats layouts, video full screen & bottom sheets animations 2024-07-15 16:10:40 +02:00
Sylvain Berfini
7ba19364b9 Improved parsing of account settings + navigate out of resuming fragment if no longer available due to config changes 2024-07-15 16:10:40 +02:00
Sylvain Berfini
dce27530e0 Fixd margin issue in call stats layout 2024-07-15 16:10:40 +02:00
Sylvain Berfini
fd8b8b5c29 Hide non-scheduled conferences from meetings list 2024-07-15 16:10:40 +02:00
Sylvain Berfini
3b626bd0a6 Updated media encryption statistics layout 2024-07-15 16:10:40 +02:00
Sylvain Berfini
37bf87e23e Switched from findLastCompletelyVisibleItemPosition() to findLastVisibleItemPosition() to prevent display issue if latest message is very long 2024-07-15 16:10:40 +02:00
Sylvain Berfini
93f1ef1aca Improved update password dialog layout 2024-07-15 16:10:40 +02:00
Sylvain Berfini
4536f917f6 Added statistics bottom sheet to conference 2024-07-15 16:10:40 +02:00
Sylvain Berfini
d7d6767361 Added joining indeterminate progress bar/label & paused icon/label on conference participants cells 2024-07-15 16:10:40 +02:00
Sylvain Berfini
a707c6c988 Added auth requested dialog 2024-07-15 16:10:40 +02:00
Sylvain Berfini
8750c2da55 Updated meeting shedule layout 2024-07-15 16:10:40 +02:00
Sylvain Berfini
308aff5392 Started call stats 2024-07-15 16:10:40 +02:00
Sylvain Berfini
a7c63c748a Remove reply action in message notification if account is not in secure mode and message is received in a unsecure conversation 2024-07-15 16:10:40 +02:00
Sylvain Berfini
8e104114f7 Enable bundle mode by default for third party SIP accounts as well 2024-07-15 16:10:40 +02:00
Sylvain Berfini
bf8d2de176 Slightly improved unsecured conversation list cell layout 2024-07-15 16:10:40 +02:00
Sylvain Berfini
7734e47742 Started inConference/joining states of participant device in conference 2024-07-15 16:10:40 +02:00
Sylvain Berfini
913d126203 Automatically switch to active speaker layout when more participant devices than authorized by grid layout are present 2024-07-15 16:10:40 +02:00
Sylvain Berfini
146b9d0cdf Fixed crash 2024-07-15 16:10:40 +02:00
Sylvain Berfini
af1ee922ad Updated SSO 2024-07-15 16:10:40 +02:00
Sylvain Berfini
06074f0490 Fixed disabled buttons color in call 2024-07-15 16:10:40 +02:00
Sylvain Berfini
e82f42e51a Improved conference pause/resume + hide video surfaces when call or conference is paused 2024-07-15 16:10:40 +02:00
Sylvain Berfini
f91252f678 Revert part of previous changes, causes issues 2024-07-15 16:10:40 +02:00
Sylvain Berfini
625edfe33a Various UI fixes 2024-07-15 16:10:40 +02:00
Sylvain Berfini
780c2f55dc Added onTrimMemory callback + trying to properly shut down Core when latest activity is being destroyed 2024-07-15 16:10:40 +02:00
Sylvain Berfini
897312831e Trying to cache native friend requests 2024-07-15 16:10:40 +02:00
Sylvain Berfini
8746a2646a Do not show when for push received notification 2024-07-15 16:10:40 +02:00
Sylvain Berfini
82d6d37fd7 Make sure to always search contacts using a cleaned SIP address + improved Android DB search for SIP address + delay loading contacts to after an account was successfully registered 2024-07-15 16:10:40 +02:00
Sylvain Berfini
aa36235ab1 Should speed up a little the first display when app starts 2024-07-15 16:10:40 +02:00
Sylvain Berfini
f9667ff2e4 Removed smooth scrolling to go to bottom 2024-07-15 16:10:40 +02:00
Sylvain Berfini
59ece2f8a8 Properly integrated SSO in app 2024-07-15 16:10:40 +02:00
Sylvain Berfini
9c3102392b Improved push notification received notification by inheriting PushService from SDK 2024-07-15 16:10:40 +02:00
Sylvain Berfini
e618992fb5 Updated contact editor to allow creating a contact with only a organization name 2024-07-15 16:10:40 +02:00
Sylvain Berfini
c7b4c14d66 Improved video activation while in audio only conference 2024-07-15 16:10:40 +02:00
Sylvain Berfini
aedd1a2577 Fixed issue with account login 2024-07-15 16:10:40 +02:00
Sylvain Berfini
f93e771bab Changed SSO redirect URI scheme, updated default SSO URI, delete saved config in case of token refresh error before retrying 2024-07-15 16:10:40 +02:00
Sylvain Berfini
bfd5a8f6fa Split login step to allow detecting if using digest or SSO authentication 2024-07-15 16:10:40 +02:00
Sylvain Berfini
935d134bbd Show paused icon instead of call quality indicator while paused or paused by remote 2024-07-15 16:10:40 +02:00
Sylvain Berfini
f9ae9985a4 Created landscape versions of meeting waiting room & conference menu 2024-07-15 16:10:40 +02:00
Sylvain Berfini
03299bae16 Fixed missing e2e encrypted header 2024-07-15 16:10:40 +02:00
Sylvain Berfini
fc82c8bcf6 Slightly improved disabled plain text chat room warning display 2024-07-15 16:10:40 +02:00
Sylvain Berfini
3882c57cfc Fixed gif display in media grid 2024-07-15 16:10:40 +02:00
Sylvain Berfini
d9b0d6c740 Factorized code 2024-07-15 16:10:40 +02:00
Sylvain Berfini
8204f6d2da Hide emoji reactions & reply action from long press menu on chat message in not encrypted conversation (if account is in secure mode) 2024-07-15 16:10:40 +02:00
Sylvain Berfini
476aec1916 Use adapter & recyclerview to display media & documents in conversation 2024-07-15 16:10:40 +02:00
Sylvain Berfini
940937a9b7 Fixed broken media viewer 2024-07-15 16:10:40 +02:00
Sylvain Berfini
598ac6cbd3 Added documents menu (like media) 2024-07-15 16:10:40 +02:00
Sylvain Berfini
109b5e71e2 Updated schedule meeting layout 2024-07-15 16:10:40 +02:00
Sylvain Berfini
9872f505b6 Prevent duplicated participants in list during meeting schedule + prevent scheduling a meeting without subject or participant + added icon to remove participant from list 2024-07-15 16:10:40 +02:00
Sylvain Berfini
2483b7b6d0 Fixed media encryption status bottom sheet in landscape 2024-07-15 16:10:40 +02:00
Sylvain Berfini
56283a1480 Improved chat bubble touch area 2024-07-15 16:10:40 +02:00
Sylvain Berfini
8a01f30a8d Prevent e2e details modale to show up while scrolling 2024-07-15 16:10:40 +02:00
Sylvain Berfini
d429f181a0 Updated chat list cell layout 2024-07-15 16:10:40 +02:00
Sylvain Berfini
ab72e5eb62 Added conversation messages decorator in secured chat room to explain and show info on click 2024-07-15 16:10:40 +02:00
Sylvain Berfini
d77f51a5e2 Prevent replying & transfering messages to read only or disabled conversations 2024-07-15 16:10:40 +02:00
Sylvain Berfini
1697dc7f7b Removed toast no longer required 2024-07-15 16:10:40 +02:00
Sylvain Berfini
012b2419e5 Show ephemeral messages icon under conversation title if they are enabled 2024-07-15 16:10:40 +02:00
Sylvain Berfini
ef53eb62ae Changed troubleshooting toggle to only enable/disable printing logs to adb logcat 2024-07-15 16:10:40 +02:00
Sylvain Berfini
2ca3d017bf Updated conversation event design 2024-07-15 16:10:40 +02:00
Sylvain Berfini
6faff7f780 Fixed going back to default fragment when rotating the device 2024-07-15 16:10:40 +02:00
Sylvain Berfini
91ec675cbe Refactored Media & File viewers 2024-07-15 16:10:40 +02:00
Sylvain Berfini
c71af9f23a Uniformized popup menus 2024-07-15 16:10:40 +02:00
Sylvain Berfini
30ab1c2196 Various fixes & improvements 2024-07-15 16:10:40 +02:00
Sylvain Berfini
3810ab4ae9 Added medias list in conversation 2024-07-15 16:10:40 +02:00
Sylvain Berfini
db486360cc Update cell signal icon & color depending on call current quality 2024-07-15 16:10:40 +02:00
Sylvain Berfini
879b6b3b7e Added empty contacts settings (for now) 2024-07-15 16:10:40 +02:00
Sylvain Berfini
3fb8b77f87 Improved contact save changes & join meeting buttons 2024-07-15 16:10:40 +02:00
Sylvain Berfini
2824c1a3f8 Fixed conference layouts not visible + trying to improve navigation 2024-07-15 16:10:40 +02:00
Sylvain Berfini
e994edbf0a Added missing long click listener for generic file chat bubble 2024-07-15 16:10:40 +02:00
Sylvain Berfini
1c7316408d Started to add content descriptions 2024-07-15 16:10:40 +02:00
Sylvain Berfini
0105f1d669 Code improvements 2024-07-15 16:10:40 +02:00
Sylvain Berfini
32145980f4 Fixed missing 'play' icon above video preview if it is the only file in a bubble + added video duration if info is available + improved onLongClick on bubbles 2024-07-15 16:10:40 +02:00
Sylvain Berfini
c62ac4359a Switched mute button background to red when selected 2024-07-15 16:10:40 +02:00
Sylvain Berfini
d002d308c4 Show all conversations but disable non-encrypted ones if in secure mode 2024-07-15 16:10:40 +02:00
Sylvain Berfini
01361bfcaa Fixed top header in call/conference for both short/long name/title 2024-07-15 16:10:40 +02:00
Sylvain Berfini
25a0f4b65a Added edit meeting fragment 2024-07-15 16:10:40 +02:00
Sylvain Berfini
65f3dd896c Added code to create group call from existing chat room 2024-07-15 16:10:40 +02:00
Sylvain Berfini
25ab474fba Added button to do ZRTP SAS validation again 2024-07-15 16:10:40 +02:00
Sylvain Berfini
e19e9bfdc7 Only show 'call can be trusted' toast when SAS is validated 2024-07-15 16:10:40 +02:00
Sylvain Berfini
523b762cac Fixed issues related to contacts being added/edited/removed 2024-07-15 16:10:40 +02:00
Sylvain Berfini
3864d54936 Reworked conference/incoming/outgoing/ended call fragments to match changes made to active call fragment 2024-07-15 16:10:40 +02:00
Sylvain Berfini
58e41d99c9 Animate caret to handle and back while in call 2024-07-15 16:10:40 +02:00
Sylvain Berfini
1c24c805df Started audio only layout for conference 2024-07-15 16:10:40 +02:00
Sylvain Berfini
784803336c Trying new in-call layout 2024-07-15 16:10:40 +02:00
Sylvain Berfini
72daf9ebd2 Hide imdn status for received messages in 1-1 conversation + always show date/time & imdn status even for grouped messages 2024-07-15 16:10:40 +02:00
Sylvain Berfini
940a6b0577 Fixed navigation status bar color for file viewer fragment 2024-07-15 16:10:40 +02:00
Sylvain Berfini
ed9df940af Fixed mute mic while in conference 2024-07-15 16:10:40 +02:00
Sylvain Berfini
d32c6f70a1 Hide active speaker miniature + fix display name missing + added missing mute indicator + added avatar if not sending video 2024-07-15 16:10:40 +02:00
Sylvain Berfini
bd2936b05e Fixed weird scrolling position in conversation after going into info and going back 2024-07-15 16:10:39 +02:00
Sylvain Berfini
52e7acb4ee Reworked a few things to speed up app cold startup 2024-07-15 16:10:39 +02:00
Sylvain Berfini
1734d11639 Fixed conversation menu icon size + prevent switching to mosaic mode when more than 6 participants while in conference 2024-07-15 16:10:39 +02:00
Sylvain Berfini
318a487e10 Restored start-up screen 2024-07-15 16:10:39 +02:00
Sylvain Berfini
ddd614acd0 Trying to speed up app startup & prevent blank screen 2024-07-15 16:10:39 +02:00
Sylvain Berfini
b1d0554a07 Minor update 2024-07-15 16:10:39 +02:00
Sylvain Berfini
254d8619fe Make sure friend lists subscruptions are enabled for sip.linphone.org accounts and disabled for others 2024-07-15 16:10:39 +02:00
Sylvain Berfini
44af8bb340 Added start group call feature from start call fragment 2024-07-15 16:10:39 +02:00
Sylvain Berfini
260ad798ed Improved message regexp 2024-07-15 16:10:39 +02:00
Sylvain Berfini
a024dd2278 Fixed received/sent audio files handled as voice recordings 2024-07-15 16:10:39 +02:00
Sylvain Berfini
ce1d3d4807 Added audio route selection to conference waiting room fragment 2024-07-15 16:10:39 +02:00
Sylvain Berfini
4697af6c27 Fixed devices order depending on conference layout + set/restore navigation status bar color when going/leaving waiting room 2024-07-15 16:10:39 +02:00
Sylvain Berfini
247f763c11 Improved scroll to today in meetings list 2024-07-15 16:10:39 +02:00
Sylvain Berfini
c8319ed014 Improved margins between chat bubbles 2024-07-15 16:10:39 +02:00
Sylvain Berfini
75d1f719ae Fixed favorites caret not switching direction in landscape + conversation title in landscape not displayed properly + show suggestions SIP addresses domain instead of just username + remove default account address from suggestions 2024-07-15 16:10:39 +02:00
Sylvain Berfini
98a3c89435 Fixed recyclerview header not colliding in some special case 2024-07-15 16:10:39 +02:00
Sylvain Berfini
9fc6a1eb57 Fixed wrong position of today indicator in meetings list 2024-07-15 16:10:39 +02:00
Sylvain Berfini
e6d5e35f29 Updated picto in calls history when call log was for a conference 2024-07-15 16:10:39 +02:00
Sylvain Berfini
8870564066 Go to waiting room when clicking on call icon in calls history if conference call log + enabled video by default in waiting room when possible 2024-07-15 16:10:39 +02:00
Sylvain Berfini
f23510da3a More work for having both grid & active speaker layout but not at the same time 2024-07-15 16:10:39 +02:00
Sylvain Berfini
bf99bd402f Updated gradle to 8.2.2 2024-07-15 16:10:39 +02:00
Sylvain Berfini
7eb756cae6 Started active speaker layout 2024-07-15 16:10:39 +02:00
Sylvain Berfini
51c6037f3f Fixed white screen when starting the app sometimes + conversations list fragment stacked twice when opening app from chat notification 2024-07-15 16:10:39 +02:00
Sylvain Berfini
d2dc99d7a1 Using newly added callback to properly handle presence & contacts reload 2024-07-15 16:10:39 +02:00
Sylvain Berfini
1299ff0f05 Added back some linphonerc_default values that were removed from 5.2 version 2024-07-15 16:10:39 +02:00
Sylvain Berfini
b82f8aed2b Fixed meeting invitation participant label with only 1 participant 2024-07-15 16:10:39 +02:00
Sylvain Berfini
fcbe629e48 Make sure there is always a 'today' indicator in meetings list 2024-07-15 16:10:39 +02:00
Sylvain Berfini
a198fab204 Fixed text watcher for @participant not working after leaving & resuming conversation 2024-07-15 16:10:39 +02:00
Sylvain Berfini
a50b74d042 Hidden some options of conversation long press menu if read only 2024-07-15 16:10:39 +02:00
Sylvain Berfini
e4a85985c2 Removed some logs 2024-07-15 16:10:39 +02:00
Sylvain Berfini
d6630e05d4 Enabled auto policy for hardware video codecs 2024-07-15 16:10:39 +02:00
Sylvain Berfini
8c068d3be7 Prevent call trusted toast to be displayed multiple times + fixed number of paused calls when > 2 2024-07-15 16:10:39 +02:00
Sylvain Berfini
3a240f107c Hiding some menus & buttons when conversation is read only 2024-07-15 16:10:39 +02:00
Sylvain Berfini
6f8469eb0b Fixed leave group not hiding send message area 2024-07-15 16:10:39 +02:00
Sylvain Berfini
ef47624b9d Only load 30 messages when opening conversation, loading more messages when scrolling up 2024-07-15 16:10:39 +02:00
Sylvain Berfini
b919f51ecb Improved conversations list computing when filter is empty 2024-07-15 16:10:39 +02:00
Sylvain Berfini
7fec6cbb75 Removed useless address cleaning, already done by SDK 2024-07-15 16:10:39 +02:00
Sylvain Berfini
dc23df8b3a More dark theme fixes 2024-07-15 16:10:39 +02:00
Sylvain Berfini
b185de70ca Added media encryption statistics when long clicking media encryption icon on top of active call 2024-07-15 16:10:39 +02:00
Sylvain Berfini
d232fa0d14 Removed cellular/wifi signal alerts 2024-07-15 16:10:39 +02:00
Sylvain Berfini
3e845f45f7 Prevent crash 2024-07-15 16:10:39 +02:00
Sylvain Berfini
8b1ff7af2b Added entries to conversation popup menu + mute indicator below conversation title 2024-07-15 16:10:39 +02:00
Sylvain Berfini
570492cea9 Fixed disable video button even in call + audio route switching from bluetooth to speaker when video was enabled 2024-07-15 16:10:39 +02:00
Sylvain Berfini
8aa17ed097 Added ourselves as participant of a conversation 2024-07-15 16:10:39 +02:00
Sylvain Berfini
e4d073471c More dark theme improvements 2024-07-15 16:10:39 +02:00
Sylvain Berfini
ae4d087ad6 Improvements for dark theme 2024-07-15 16:10:39 +02:00
Sylvain Berfini
b1e5e45b43 Improved themes 2024-07-15 16:10:39 +02:00
Sylvain Berfini
eb6ab49543 Disable video toggle while outgoing call is ringing 2024-07-15 16:10:39 +02:00
Sylvain Berfini
834f8f7d7e Removed toast when entering conversation + improved chat message notification pending intent 2024-07-15 16:10:39 +02:00
Sylvain Berfini
43d5e8ff23 Fixed click on call notification not opening in-call activity 2024-07-15 16:10:39 +02:00
Sylvain Berfini
1d122abb17 Updated colors on some parts while in call 2024-07-15 16:10:39 +02:00
Sylvain Berfini
83070a9c01 Do not show the ZRTP SAS dialog again when clicking on wrong letters 2024-07-15 16:10:39 +02:00
Sylvain Berfini
b308e2a8a0 Revert hiding some features 2024-07-15 16:10:39 +02:00
Sylvain Berfini
c447b2699c Changes regarding call notifications 2024-07-15 16:10:39 +02:00
Sylvain Berfini
43c22b8ed5 Hidden for now account mode selection, moved push notification toggle from account settings to account profile 2024-07-15 16:10:39 +02:00
Sylvain Berfini
8c7ca490c8 Hidden & disabled some features for now 2024-07-15 16:10:39 +02:00
Sylvain Berfini
e8da60cb77 Updated license year 2024-07-15 16:10:39 +02:00
Sylvain Berfini
110ff995f8 Storing & using international prefix iso country code 2024-07-15 16:10:39 +02:00
Sylvain Berfini
c8a20f4f57 Prevent input error causing crash 2024-07-15 16:10:39 +02:00
Sylvain Berfini
f3ab328d74 Fixed download progress for message sent & synchronized on another device 2024-07-15 16:10:39 +02:00
Sylvain Berfini
82f1e1b486 Remove display name part of call transfer related toasts 2024-07-15 16:10:39 +02:00
Sylvain Berfini
4b2058fbe6 Show blue toast when entering an end-to-end encrypted conversation 2024-07-15 16:10:39 +02:00
Sylvain Berfini
6f623ae080 Shorten durating for call ended screen if terminate call action was made by user 2024-07-15 16:10:39 +02:00
Sylvain Berfini
715375831a Updated layout for meeting participant picker 2024-07-15 16:10:39 +02:00
Sylvain Berfini
3e0cc865b0 Clear filter after selecting an address 2024-07-15 16:10:39 +02:00
Sylvain Berfini
7c462cbb64 Fixed scrolling when opening keyboard in chat + prevent duplicated or missing messages 2024-07-15 16:10:39 +02:00
Sylvain Berfini
c5e7b4c8a2 Update isRead flag in message model when chat room is marked as read 2024-07-15 16:10:39 +02:00
Sylvain Berfini
a00409e003 Added unread message indicator when scrolling up in conversation 2024-07-15 16:10:39 +02:00
Sylvain Berfini
acfabaae23 Revert changes breaking app version & incoming call go to call activity when answering 2024-07-15 16:10:39 +02:00
Sylvain Berfini
4dc660a52d Handle possible null call in alerts 2024-07-15 16:10:39 +02:00
Sylvain Berfini
ee8c36f70b Getting rid of two TODOs regarding asking permissions during call 2024-07-15 16:10:39 +02:00
Sylvain Berfini
fa5eb6a285 Improved build.gradle a bit 2024-07-15 16:10:39 +02:00
Sylvain Berfini
281b44a240 Added conversations & meetings settings 2024-07-15 16:10:39 +02:00
Sylvain Berfini
6c03a6fb7a Fixed crash when failing to connect third party sip account 2024-07-15 16:10:39 +02:00
Sylvain Berfini
8a6a2bef02 Added label to let user know there is no message matching it's filter 2024-07-15 16:10:39 +02:00
Sylvain Berfini
29e4bb5932 Started chat & meetings settings, improved other settings 2024-07-15 16:10:39 +02:00
Sylvain Berfini
70e98dfe78 Hide video related icons when feature disabled in core + prevent two seconds delay for outgoing call fragment to display info by starting it sooner (OutgoingInit instead of OutgoingProgress) 2024-07-15 16:10:39 +02:00
Sylvain Berfini
6ab9f4232b Updated some values in default/factory RCs 2024-07-15 16:10:39 +02:00
Sylvain Berfini
e98ccdc580 Improved how chat room mark as read is handled 2024-07-15 16:10:39 +02:00
Sylvain Berfini
e1c1be3e50 Fixed click on notification no going into conversation 2024-07-15 16:10:39 +02:00
Sylvain Berfini
a547a04258 Removed + when long pressing 0 in in-call numpad 2024-07-15 16:10:39 +02:00
Sylvain Berfini
0b90af9ccc Clicking on the avatar of a selected participant will remove it as well as the cross 2024-07-15 16:10:39 +02:00
Sylvain Berfini
615185deb9 Updated layout & flow for group conversation creation + all floating action buttons are now on orange background 2024-07-15 16:10:39 +02:00
Sylvain Berfini
d82eac6175 Fixed numpad bottom sheet visible after switching fullscreen mode in video call 2024-07-15 16:10:39 +02:00
Sylvain Berfini
b87a3dd92c Display meeting subject in message description 2024-07-15 16:10:39 +02:00
Sylvain Berfini
cc403f2624 Also search in contact name when filtering conversations list + updated gradle 2024-07-15 16:10:39 +02:00
Sylvain Berfini
61c85128e8 Mark chat room as read when sending a new message to update getFirstUnreadMessagePosition() in conversation adapter 2024-07-15 16:10:39 +02:00
Sylvain Berfini
ac7e19144d Allow for no selected dial plan in Account's profile 2024-07-15 16:10:39 +02:00
Sylvain Berfini
8fb87b18e8 Fixed crash when touching multiple calls top bar if not on active call fragment + fixed top bar alert & status bar color after terminating more than one call 2024-07-15 16:10:39 +02:00
Sylvain Berfini
bf089193d4 Properly close any sliding pane child fragment when default account changes 2024-07-15 16:10:39 +02:00
Sylvain Berfini
c8b1231322 Improved settings layout 2024-07-15 16:10:39 +02:00
Sylvain Berfini
be3f6ea301 Fixed group chat room shortcut generation 2024-07-15 16:10:39 +02:00
Sylvain Berfini
4330b814a6 Switched SDK to 5.4 2024-07-15 16:10:39 +02:00
Sylvain Berfini
1cab186403 Moved TTFD to end of splash screen 2024-07-15 16:10:39 +02:00
Sylvain Berfini
5ee5982e3c Generate conversations & conferences avatars on worker thread & save result in cache directory 2024-07-15 16:10:39 +02:00
Sylvain Berfini
03ee116ed4 Updated ImageUtils.getBitmapFromMultipleAvatars() to make it synchronous, fixing avatar in conversations list when quickly scrolling + shortcuts for group conversation 2024-07-15 16:10:39 +02:00
Sylvain Berfini
f702054ac4 Added navigating to a mentionned contact on click in chat bubble + low media volume alert when playing voice recording in chat bubble 2024-07-15 16:10:39 +02:00
Sylvain Berfini
4bf0a2fa5d Updated conversation top bar layout 2024-07-15 16:10:39 +02:00
Sylvain Berfini
a2b86ff5f6 Fixed outgoing message file upload display 2024-07-15 16:10:39 +02:00
Sylvain Berfini
1b827bcf76 Fixed menu button showing in '@' participants menu 2024-07-15 16:10:39 +02:00
Sylvain Berfini
6338fb65d1 Various fixes & improvements related to chat room shorcuts 2024-07-15 16:10:39 +02:00
Sylvain Berfini
70b1c67f90 Fixed sliding pane staying open on empty fragment is back gesture done while it was opening 2024-07-15 16:10:39 +02:00
Sylvain Berfini
eaa55ab068 Added button to take photos in chat directly 2024-07-15 16:10:38 +02:00
Sylvain Berfini
62c23c248f Bumped dependencies 2024-07-15 16:10:38 +02:00
Sylvain Berfini
25c2cfc84e Added button to scroll to bottom 2024-07-15 16:10:38 +02:00
Sylvain Berfini
4ac3649c90 Fixed crash due to nav graph not loaded 2024-07-15 16:10:38 +02:00
Sylvain Berfini
10b9044aa8 Fixed scrolling issue due to MessageModel staying not read + scroll to bottom when keyboard is opened if no scrolling was initiated by user 2024-07-15 16:10:38 +02:00
Sylvain Berfini
eb5f985712 Getting rid of scroll to bottom code & events that are no longer required 2024-07-15 16:10:38 +02:00
Sylvain Berfini
62ea993847 No longer use conversation send area as a bottom sheet 2024-07-15 16:10:38 +02:00
Sylvain Berfini
73fda1fb25 Fixed generated APK name & .gitlab-ci artifact path 2024-07-15 16:10:38 +02:00
Sylvain Berfini
919abd3bd3 Fixed CI build to due debug libs not found, disabling crashlytics upload in that case 2024-07-15 16:10:38 +02:00
Sylvain Berfini
5d3d8eeedc Added plain text file viewer 2024-07-15 16:10:38 +02:00
Sylvain Berfini
de2f247c5f Added Crashlytics to debug build 2024-07-15 16:10:38 +02:00
Sylvain Berfini
d52c12606f Added message forward + a few small improvements 2024-07-15 16:10:38 +02:00
Sylvain Berfini
678949aff2 Added toast to let user know when files are waiting for a conversation to be opened to add files in it 2024-07-15 16:10:38 +02:00
Sylvain Berfini
d5b0d82adc Added forwarded message indicator 2024-07-15 16:10:38 +02:00
Sylvain Berfini
637f424a70 Fixed foreground service types & use 2024-07-15 16:10:38 +02:00
Sylvain Berfini
2274cdd343 Try to open unsupported files in native app 2024-07-15 16:10:38 +02:00
Sylvain Berfini
aa34132047 Replaced dots by current page / page count in PDF viewer 2024-07-15 16:10:38 +02:00
Sylvain Berfini
54b9ae8cd4 Detect & improve broken files if possible 2024-07-15 16:10:38 +02:00
Sylvain Berfini
c6b7ed0ef3 Added logs & small fixes 2024-07-15 16:10:38 +02:00
Sylvain Berfini
6645d579a2 Added audio device callback to reload sound devices when needed 2024-07-15 16:10:38 +02:00
Sylvain Berfini
ec07d54ed9 Improved long press menu on chat bubble when text was very long 2024-07-15 16:10:38 +02:00
Sylvain Berfini
2ad4e76bc3 Fixed clicking on chat message notification while being in conversations list stacked it twice 2024-07-15 16:10:38 +02:00
Sylvain Berfini
7c51cf7588 Added sharing file from file viewer 2024-07-15 16:10:38 +02:00
Sylvain Berfini
b32007b1ca Added export to PDF file received by chat to Android public storage 2024-07-15 16:10:38 +02:00
Sylvain Berfini
80b887c874 Improved PDF viewer 2024-07-15 16:10:38 +02:00
Sylvain Berfini
ff521eb559 Prevent bottom sheet dialog to hide some long press options (mostly happened in landscape) 2024-07-15 16:10:38 +02:00
Sylvain Berfini
e9b1bfd2a0 Fixed first sliding pane child fragment not being poped-up when closing sliding pane, manually navigating to empty fragment to workaround that 2024-07-15 16:10:38 +02:00
Sylvain Berfini
9eb4458c73 Close sliding pane when default account changed 2024-07-15 16:10:38 +02:00
Sylvain Berfini
e9f19b6834 Added toast when new account is successfully configured + set first available account as default when previous one was removed 2024-07-15 16:10:38 +02:00
Sylvain Berfini
fa796b9609 Updated way of setting light/dark/auto mode 2024-07-15 16:10:38 +02:00
Sylvain Berfini
e6d33a9e1a Fixed timestamps in message notification 2024-07-15 16:10:38 +02:00
Sylvain Berfini
6f9a5a6009 Small improvements over reactions layout 2024-07-15 16:10:38 +02:00
Sylvain Berfini
d178ce40b5 Fixed & improved back press callbacks set by app 2024-07-15 16:10:38 +02:00
Sylvain Berfini
ddc8ba7105 Scroll to first unread message in conversation upon entering it 2024-07-15 16:10:38 +02:00
Sylvain Berfini
006fa3fa4a This should prevent empty fragment visible sometimes 2024-07-15 16:10:38 +02:00
Sylvain Berfini
fd088fdbc8 Fixed double back gesture required to leave conversations list when app is opened from shortcut 2024-07-15 16:10:38 +02:00
Sylvain Berfini
ef1669dd4c Fetch all unread messages before creating a new notifiable for a chat room 2024-07-15 16:10:38 +02:00
Sylvain Berfini
58ec0e7abb Fixed & improved a few things related to chat 2024-07-15 16:10:38 +02:00
Sylvain Berfini
f5d141d59f Added app version (git describe) to troubleshooting view 2024-07-15 16:10:38 +02:00
Sylvain Berfini
b30f44a0a1 Do not display confirm dialog when leaving new contact fragment without having typed anything 2024-07-15 16:10:38 +02:00
Sylvain Berfini
02b372fa3d Reset some message model fields when re-computing contents list 2024-07-15 16:10:38 +02:00
Sylvain Berfini
aab9704d24 Added onAccountRemoved + fixed FAB background color + removed setFriendsDatabasePath that no longer exists 2024-07-15 16:10:38 +02:00
Sylvain Berfini
9b69074352 Updated colors in main call views to keep the same color no matter the light/dark theme 2024-07-15 16:10:38 +02:00
Sylvain Berfini
9702118f3f Bumped dependencies 2024-07-15 16:10:38 +02:00
Sylvain Berfini
ee04b728c9 Do not use color night variant, instead use attr & theme 2024-07-15 16:10:38 +02:00
Sylvain Berfini
b80a86a366 Added full screen intent permission for Android 14 + updated callbacks due to rework 2024-07-15 16:10:38 +02:00
Sylvain Berfini
3e7e2000d5 Fixed favourite status in contacts list not matching reality after a change 2024-07-15 16:10:38 +02:00
Sylvain Berfini
9070b77b30 Updated previous & next messages bubbles if needed when a message is deleted 2024-07-15 16:10:38 +02:00
Sylvain Berfini
99445cc8d6 Updated events layout & send message icon 2024-07-15 16:10:38 +02:00
Sylvain Berfini
bfc2a8ae34 Improved scrolling performances in contacts list 2024-07-15 16:10:38 +02:00
Sylvain Berfini
6badcc2887 Fixed crash when newly received/sent message should update group with last one in history + improved outgoing bubble background 2024-07-15 16:10:38 +02:00
Sylvain Berfini
b10b51f839 Do update conference layout in dialog 2024-07-15 16:10:38 +02:00
Sylvain Berfini
9496999773 Added back intent filter for dialing tel, sip or sips URI 2024-07-15 16:10:38 +02:00
Sylvain Berfini
c77df7228e Added image placeholder, restore round corners except for gifs 2024-07-15 16:10:38 +02:00
Sylvain Berfini
fdc696691a Updated dependencies 2024-07-15 16:10:38 +02:00
Sylvain Berfini
ff2d04351f Renamed chat message to simply message 2024-07-15 16:10:38 +02:00
Sylvain Berfini
45ca7aa348 Proper fix for chatroom-xxxx + updated logs to use conversation instead of chat room 2024-07-15 16:10:38 +02:00
Sylvain Berfini
24cbcd3937 Updated call transfer icon 2024-07-15 16:10:38 +02:00
Sylvain Berfini
489aece7c5 Dismiss dialogs when fragment is paused to prevent crashes when rotating device 2024-07-15 16:10:38 +02:00
Sylvain Berfini
39a6254ee1 Refresh chat room info when state is going to Created, prevents chatroom-xxxx display 2024-07-15 16:10:38 +02:00
Sylvain Berfini
0bbab221a2 Updated disabled buttons when in pause 2024-07-15 16:10:38 +02:00
Sylvain Berfini
9094a167eb Fixed bubble UI when scrolling after a voice record has been displayed 2024-07-15 16:10:38 +02:00
Sylvain Berfini
39b8358ef5 Fixed reply through slide action not opening keyboard 2024-07-15 16:10:38 +02:00
Sylvain Berfini
a571b77117 Started conference layout bottom sheet 2024-07-15 16:10:38 +02:00
Sylvain Berfini
fd3f85f2b7 Reworked audio devices list in call 2024-07-15 16:10:38 +02:00
Sylvain Berfini
55d67e92d3 Reworked light/dark theme setting 2024-07-15 16:10:38 +02:00
Sylvain Berfini
a85d0df668 Quickly added dark mode from Figma, still a lot of work to do... 2024-07-15 16:10:38 +02:00
Sylvain Berfini
f623de53d4 Workaround to display basic/not secured conversation if unread count > 0 2024-07-15 16:10:38 +02:00
Sylvain Berfini
5aaa174b20 Improved conference call notification + new conference call log details layout 2024-07-15 16:10:38 +02:00
Sylvain Berfini
c273fc451a Added orange border to chat bubble to show selected bubble when opening bottom sheet menu + small refactoring 2024-07-15 16:10:38 +02:00
Sylvain Berfini
62fa1d532c Started improvement over notify received to prevent blinking lists 2024-07-15 16:10:38 +02:00
Sylvain Berfini
92835a1e10 Fixed issue with first letter displayed when refreshing contacts list 2024-07-15 16:10:38 +02:00
Sylvain Berfini
20fbeda124 Improved avatars cache 2024-07-15 16:10:38 +02:00
Sylvain Berfini
56bc96314a Using same color for dialog scrim as Android 2024-07-15 16:10:38 +02:00
Sylvain Berfini
688f797acf Use better quality of native contact picture if available, otherwise fallback to thumbnail 2024-07-15 16:10:38 +02:00
Sylvain Berfini
5f9edb4fcc Added swipe left to right on a chat bubble to reply 2024-07-15 16:10:38 +02:00
Sylvain Berfini
f35df5e418 Update conversation subject when leaving info fragment + removed todos, replaced by toasts 2024-07-15 16:10:38 +02:00
Sylvain Berfini
a64e13a021 Fixed wrong status bar color when non-default account registration fails 2024-07-15 16:10:38 +02:00
Sylvain Berfini
294f7f6fae Added export file to Android's MediaStore from FileViewer 2024-07-15 16:10:38 +02:00
Sylvain Berfini
746ddf6457 Fixed some TODOs 2024-07-15 16:10:38 +02:00
Sylvain Berfini
373a5f004b Replaced top bar check icon by big bottom button, fixed conversation users_three icon instead of meeting 2024-07-15 16:10:37 +02:00
Sylvain Berfini
ad35f85c3a Added compatibility package 2024-07-15 16:10:37 +02:00
Sylvain Berfini
0d31bfe3c3 Updated layout for default/interop mode selection + added logs to contacts manager 2024-07-15 16:10:37 +02:00
Sylvain Berfini
bd9947a705 Display in_progress drawable + text on chat rooms list cell to show removal is in progress 2024-07-15 16:10:37 +02:00
Sylvain Berfini
d30f7ba5ba Fixed deadlock when receiving call 2024-07-15 16:10:37 +02:00
Sylvain Berfini
3ea3ff288b Added schedule meeting from conversation info, fixed previous participants removed when adding new participant(s) to a meeting schedule 2024-07-15 16:10:37 +02:00
Sylvain Berfini
4e602fc5e8 Fixed sharing logs with Linphone from debug fragment 2024-07-15 16:10:37 +02:00
Sylvain Berfini
7099a33dc4 Added logo on configure chat messages ephemeral duration + fixed welcome linphone logo 2024-07-15 16:10:37 +02:00
Sylvain Berfini
ee15b00533 Fixed chat rooms list not re-ordering properly 2024-07-15 16:10:37 +02:00
Sylvain Berfini
d480840353 Added see contact / add to contacts action in 1-1 conversation info 2024-07-15 16:10:37 +02:00
Sylvain Berfini
16dd423016 Reworked ephemeral lifetime configuration dialog into a dedicated fragment 2024-07-15 16:10:37 +02:00
Sylvain Berfini
fb05cf6280 Fixed unread message count not updated in chat room list cell when a new message was received 2024-07-15 16:10:37 +02:00
Sylvain Berfini
c2eccd23a8 Open keyboard on reply, scroll to latest message when keyboard is opened, removed onNewEvents causing duplicated messages, fixed latest message display when adding new ones that should be grouped with it 2024-07-15 16:10:37 +02:00
Sylvain Berfini
ed23268672 Updated meeting icon + using newly added callbacks in SDK for default account 2024-07-15 16:10:37 +02:00
Sylvain Berfini
975473b2e4 Fixed issue in ephemeral lifetime dialog 2024-07-15 16:10:37 +02:00
Sylvain Berfini
c0707e8cb5 Fixed action in contact details 2024-07-15 16:10:37 +02:00
Sylvain Berfini
d58edf0614 Fixed delete conversation history not updating chat messages list after going back 2024-07-15 16:10:37 +02:00
Sylvain Berfini
ce1cb3a15a Fixed issue with suggestion initials display + fixed animated in_progress 2024-07-15 16:10:37 +02:00
Sylvain Berfini
c650887c04 More work related to ephemeral messages 2024-07-15 16:10:37 +02:00
Sylvain Berfini
991a5e695a Added missed call(s) notification 2024-07-15 16:10:37 +02:00
Sylvain Berfini
2f18ecb562 Added auto downloaded files to notifications 2024-07-15 16:10:37 +02:00
Sylvain Berfini
370b786ed0 Remove chat room shortcut after deleting it 2024-07-15 16:10:37 +02:00
Sylvain Berfini
a092922145 Code cleanup 2024-07-15 16:10:37 +02:00
Sylvain Berfini
ff6d722e44 Attempt to fix random dead lock when app starts 2024-07-15 16:10:37 +02:00
Sylvain Berfini
d325203e8b Fixed in-call avatar not updated when ZRTP SAS is validated + scroll to top of calls logs history on resume 2024-07-15 16:10:37 +02:00
Sylvain Berfini
c11567f095 Bumped gradle to 8.1.4 2024-07-15 16:10:37 +02:00
Sylvain Berfini
bd38c7dc49 Added ephemeral messages to conversation info 2024-07-15 16:10:37 +02:00
Sylvain Berfini
80994ffafb Added chat message re-send action on long press if outgoing and in NotDelivered state 2024-07-15 16:10:37 +02:00
Sylvain Berfini
a1374d228e Changes for first launches & proper back nav 2024-07-15 16:10:37 +02:00
Sylvain Berfini
c491f18ba5 Fixed hanging up call from notification 2024-07-15 16:10:37 +02:00
Sylvain Berfini
c7f86311aa Reworked top bar alert mechanism, added network not reachable alert 2024-07-15 16:10:37 +02:00
Sylvain Berfini
6b95cc6a5c Added long press bottom sheet menu to meetings list cell 2024-07-15 16:10:37 +02:00
Sylvain Berfini
3ce702fc0d Updated conversation info participant cell with missing menu + added debug logs 2024-07-15 16:10:37 +02:00
Sylvain Berfini
6c72fb9689 Moved FileViewerFragment out of chat nav scope to be able to display it fullscreen when in landscape 2024-07-15 16:10:37 +02:00
Sylvain Berfini
7cca8f1889 Fixed subject changed event using chat room username insteadf of subject 2024-07-15 16:10:37 +02:00
Sylvain Berfini
c92d4982c6 Using different regex for URL detection to ensure it starts by http(s) and thus prevents ActivityNotFound exception 2024-07-15 16:10:37 +02:00
Sylvain Berfini
5f5885cb18 Fixed race condition issue on avatarsMap + listener removed by mistake 2024-07-15 16:10:37 +02:00
Sylvain Berfini
b113e2b729 Fixed issues when switching default account 2024-07-15 16:10:37 +02:00
Sylvain Berfini
8d05d786ce Added video player 2024-07-15 16:10:37 +02:00
Sylvain Berfini
b6c146f123 Added PDF file viewer 2024-07-15 16:10:37 +02:00
Sylvain Berfini
73229f51a1 Ask for record audio permission when trying to record a voice message if not granted yet 2024-07-15 16:10:37 +02:00
Sylvain Berfini
26b3fe67a3 Updated adapters (no longer need viewlifecycleowner) & improved avatar loader 2024-07-15 16:10:37 +02:00
Sylvain Berfini
01dab1613d More layout improvements, fixed nested recyclerview invisible in landscape 2024-07-15 16:10:37 +02:00
Sylvain Berfini
6f4e1a45d1 Reworked main layouts with recyclerview to improve scrolling performances (like in chat) 2024-07-15 16:10:37 +02:00
Sylvain Berfini
297eb71ff7 Various UI fixes 2024-07-15 16:10:37 +02:00
Sylvain Berfini
b66a40fa41 Fixed contacts list issue when changing filter 2024-07-15 16:10:37 +02:00
Sylvain Berfini
ac1ae71f11 Fixed bad display of chat message reply if original message is missing 2024-07-15 16:10:37 +02:00
Sylvain Berfini
00cea06899 Improved processing of contacts in contacts list 2024-07-15 16:10:37 +02:00
Sylvain Berfini
07926bad1e Fixed all participants displayed as admin in video conference 2024-07-15 16:10:37 +02:00
Sylvain Berfini
57eb506bdf Updated share icon behavior in meeting info + added new item in popup menu 2024-07-15 16:10:37 +02:00
Sylvain Berfini
5cc8407077 Updated chat message in progress status icon (now animated) 2024-07-15 16:10:37 +02:00
Sylvain Berfini
f1eca63b5a Updated style of login/register bottom button in assistant 2024-07-15 16:10:37 +02:00
Sylvain Berfini
ac1e636caa Disable back gesture/button while on EndedCall fragment to prevent user from leaving it until timer has expired 2024-07-15 16:10:37 +02:00
Sylvain Berfini
567ef561c0 Generate avatar for conferences based on participants avatar (like for group conversations) 2024-07-15 16:10:37 +02:00
Sylvain Berfini
035738f4c5 Proper display of group conversation events 2024-07-15 16:10:37 +02:00
Sylvain Berfini
61bd2967b0 Fixed & improved UI 2024-07-15 16:10:37 +02:00
Sylvain Berfini
2fa856e790 Added file download 2024-07-15 16:10:37 +02:00
Sylvain Berfini
a8aa3be08a Added layout for not downloaded file 2024-07-15 16:10:37 +02:00
Sylvain Berfini
6ae57158b7 Made chat message bottom sheet content dynamic 2024-07-15 16:10:37 +02:00
Sylvain Berfini
6fec1958b8 Added file content layout in bubble 2024-07-15 16:10:37 +02:00
Sylvain Berfini
efdfc809bc Made call history detail fragment scrollable, removed viewLifecycleOwner param from adapters that do not require it 2024-07-15 16:10:37 +02:00
Sylvain Berfini
e62b1b4999 Added missing plain/text sharing feature + proper sliding pane layout sizes in landscape 2024-07-15 16:10:37 +02:00
Sylvain Berfini
7f10548ecb Removed unseless Fragment only containing SlidingPaneLayout + using same values for SlidingPaneLayout childs width as on previous releases 2024-07-15 16:10:37 +02:00
Sylvain Berfini
53ca7aef1a Started splashscreen, waiting for first fragment to be displayed 2024-07-15 16:10:37 +02:00
Sylvain Berfini
6aa2f49321 Fixed ANR related to voice record player + other related improvements 2024-07-15 16:10:37 +02:00
Sylvain Berfini
e0d07c80ac Fixed date time & imdn icon alignement on outgoing bubbles 2024-07-15 16:10:37 +02:00
Sylvain Berfini
fb5d89e987 Added voice record player in chat bubble 2024-07-15 16:10:37 +02:00
Sylvain Berfini
f84f42d8bd Moved voice recording layout to it's own file 2024-07-15 16:10:37 +02:00
Sylvain Berfini
c28433688a Added voice record player 2024-07-15 16:10:37 +02:00
Sylvain Berfini
80fe93c6c4 Added voice recording, have to do voice record player 2024-07-15 16:10:37 +02:00
Sylvain Berfini
3071c079ba Split ConversationViewModel in two 2024-07-15 16:10:37 +02:00
Sylvain Berfini
8f33f1f0c9 Started voice recording layout 2024-07-15 16:10:37 +02:00
Sylvain Berfini
f31209162a Added some space between bottom sheet and call area 2024-07-15 16:10:37 +02:00
Sylvain Berfini
c1d76bbd29 Renamed some views, replaced caret by handle for in call bottom sheet 2024-07-15 16:10:37 +02:00
Sylvain Berfini
3507117cff Fixed small code issue 2024-07-15 16:10:37 +02:00
Sylvain Berfini
efb8ec66a2 Commented out postpone enter transition for conversation fragment, seems better 2024-07-15 16:10:37 +02:00
Sylvain Berfini
97cae93bb5 Started external file sharing 2024-07-15 16:10:37 +02:00
Sylvain Berfini
16bf6bfc2c Finished sending files through chat 2024-07-15 16:10:37 +02:00
Sylvain Berfini
116ca3cbfe Reworked participants list for mentions a bit, added possibility to attach/remove files to/from sending area 2024-07-15 16:10:37 +02:00
Sylvain Berfini
713e048db9 Much better scrolling performances in chat messages 2024-07-15 16:10:37 +02:00
Sylvain Berfini
1f18b6b0da Fixed group avatar if only one available image 2024-07-15 16:10:37 +02:00
Sylvain Berfini
5eb53725fb Few changes 2024-07-15 16:10:37 +02:00
Sylvain Berfini
dab462de35 Fixed chat rooms list sort order + added mentions menu when typing '@' + hide participants in non-group conversation 2024-07-15 16:10:37 +02:00
Sylvain Berfini
d895fc6a09 Added click on web links to open browser in chat bubble 2024-07-15 16:10:37 +02:00
Sylvain Berfini
1c7fe3fd3e Re-order conversations list when needed + scroll to bottom when a new message is sent or received in a conversation 2024-07-15 16:10:37 +02:00
Sylvain Berfini
8cbe832a67 Updated gradle to 8.1.3, added missing input type to chat text field & correct default icon for group chat rooms 2024-07-15 16:10:37 +02:00
Sylvain Berfini
e61a6a0b7f Improved reply layout 2024-07-15 16:10:37 +02:00
Sylvain Berfini
cd3b9e1422 Various UI improvements 2024-07-15 16:10:37 +02:00
Sylvain Berfini
76b41b693b Move as much as possible code from onBindViewHolder to onCreateViewHolder 2024-07-15 16:10:37 +02:00
Sylvain Berfini
115ce8148a More performance improvements 2024-07-15 16:10:37 +02:00
Sylvain Berfini
1fbad779af Performance improvements for chat 2024-07-15 16:10:37 +02:00
Sylvain Berfini
fa78f7b9b3 Updated forward/reply icons + fixed long press on text in bubble not working 2024-07-15 16:10:37 +02:00
Sylvain Berfini
b4a52e244d Updated margins on chat bubble 2024-07-15 16:10:37 +02:00
Sylvain Berfini
46ae326781 Improved chat image viewer 2024-07-15 16:10:37 +02:00
Sylvain Berfini
af03b30352 Fixed trusted icon visible in account profile if not in secure mode, hardcoded texts, wrong conference subject displayed in calls history list 2024-07-15 16:10:37 +02:00
Sylvain Berfini
2857378c87 Added conference info in chat bubble layout 2024-07-15 16:10:37 +02:00
Sylvain Berfini
1723525077 Keep scroll position in conversation when going back after leaving 2024-07-15 16:10:37 +02:00
Sylvain Berfini
ad1625dbb3 Started image viewer 2024-07-15 16:10:37 +02:00
Sylvain Berfini
b585ba7a8b Updated chat bubbles to display images grid 2024-07-15 16:10:37 +02:00
Sylvain Berfini
cafa301ea8 Started grid layout 2024-07-15 16:10:37 +02:00
Sylvain Berfini
7f739a4bc1 Displaying image (or video preview) in chat bubble if alone 2024-07-15 16:10:36 +02:00
Sylvain Berfini
59c7140ce6 Do not repeat SIP / Phone header in contact editor 2024-07-15 16:10:36 +02:00
Sylvain Berfini
aeaa41fcbe Fixed crash when rotating device while new contact editor is opened 2024-07-15 16:10:36 +02:00
Sylvain Berfini
223c91b6b9 Added long press menu on outgoing chat bubbles 2024-07-15 16:10:36 +02:00
Sylvain Berfini
30f9f381cd Added click to remove our own reaction 2024-07-15 16:10:36 +02:00
Sylvain Berfini
7aed1d83e3 Started mention of participant in chat message 2024-07-15 16:10:36 +02:00
Sylvain Berfini
5e069033b3 Updated bubbles color + added call on click in SIP URI in chat 2024-07-15 16:10:36 +02:00
Sylvain Berfini
a919f5edbc Added single sign on using OpenID and our Keycloak test instance 2024-07-15 16:10:36 +02:00
Sylvain Berfini
325feb5637 It seems that annotating the lambda makes the thread check works 2024-07-15 16:10:36 +02:00
Sylvain Berfini
61bd3978a4 Fixed crash due to .value = called from WorkerThread 2024-07-15 16:10:36 +02:00
Sylvain Berfini
56ec0f8911 Added calls list while in conference + started conference participants list 2024-07-15 16:10:36 +02:00
Sylvain Berfini
48baed897c More UI work on conference related screens 2024-07-15 16:10:36 +02:00
Sylvain Berfini
aa52f3d2b5 Started to add video to conference participants + fixed call ended fragment timer 2024-07-15 16:10:36 +02:00
Sylvain Berfini
2eb8b496cd Various fixes & improvements 2024-07-15 16:10:36 +02:00
Sylvain Berfini
178aae3883 Improved conference call log display in history (avatar to improve) 2024-07-15 16:10:36 +02:00
Sylvain Berfini
17a4e546a5 Started mosaic display for conferences 2024-07-15 16:10:36 +02:00
Sylvain Berfini
46dc3b2d00 Added missing trust badge on some call views 2024-07-15 16:10:36 +02:00
Sylvain Berfini
6dea7c3fec Create if necessary then go to 1-1 chat room when using message button from contact/history details 2024-07-15 16:10:36 +02:00
Sylvain Berfini
425722ef65 Started conference extra actions menu 2024-07-15 16:10:36 +02:00
Sylvain Berfini
36f57be6cc Rework to factorize code 2024-07-15 16:10:36 +02:00
Sylvain Berfini
fadc6032fb Updated shape of FAB + updated chat icon in bottom nav bar 2024-07-15 16:10:36 +02:00
Sylvain Berfini
85f97b86da Added possibility to send reply & scroll to original message on click 2024-07-15 16:10:36 +02:00
Sylvain Berfini
e8ca20a7e2 Started conference call UI 2024-07-15 16:10:36 +02:00
Sylvain Berfini
4368e1a5f7 Indicator in meetings list for today 2024-07-15 16:10:36 +02:00
Sylvain Berfini
a129cc5f95 Started conference waiting room 2024-07-15 16:10:36 +02:00
Sylvain Berfini
98488e5798 Added call button (does nothing yet) in chat room info + updated reactions layout 2024-07-15 16:10:36 +02:00
Sylvain Berfini
b80f520162 Fixed unecessary reloading of nav graph when app is going from background to foreground 2024-07-15 16:10:36 +02:00
Sylvain Berfini
1d7ca67053 Changes to make chat room shortcuts work 2024-07-15 16:10:36 +02:00
Sylvain Berfini
58c30a638f Started to display incoming messages reply to message 2024-07-15 16:10:36 +02:00
Sylvain Berfini
077e625512 Show message bottom sheet for 1-1 chat room 2024-07-15 16:10:36 +02:00
Sylvain Berfini
c166c87479 Reworked emoji reaction picker & display 2024-07-15 16:10:36 +02:00
Sylvain Berfini
19a15bedfa Meetings can be scheduled 2024-07-15 16:10:36 +02:00
Sylvain Berfini
cf41bef449 Allow to edit subject of group chat room 2024-07-15 16:10:36 +02:00
Sylvain Berfini
a27cf28544 Adding participants to existing group 2024-07-15 16:10:36 +02:00
Sylvain Berfini
d9d7508292 Improved genericity related to contact / suggestion picker 2024-07-15 16:10:36 +02:00
Sylvain Berfini
5847e7d2c2 Fixed new call / new chat room fragments never visible due to empty contact list 2024-07-15 16:10:36 +02:00
Sylvain Berfini
f855426a9f Made chat message delivery bottom sheet generic to also use it to display emoji reactions lists 2024-07-15 16:10:36 +02:00
Sylvain Berfini
18f0d9109e Fixed broken conference factory URI due to dumb mistake 2024-07-15 16:10:36 +02:00
Sylvain Berfini
a78421d79a Fixed incoming call style notification not always displayed 2024-07-15 16:10:36 +02:00
Sylvain Berfini
a49cb0935d Started add participant to existing group chat room 2024-07-15 16:10:36 +02:00
Sylvain Berfini
27a408c9f1 Improved avatar geneator to be able to create Icons for notifications & shortcuts 2024-07-15 16:10:36 +02:00
Sylvain Berfini
682aaafc85 Added shortcuts + improved bubble layouts 2024-07-15 16:10:36 +02:00
Sylvain Berfini
636a0b5c4d Hide & show again bottom sheet dialog when switching message 2024-07-15 16:10:36 +02:00
Sylvain Berfini
e24d3ca33f Fixed conversation not visible after creating it + missing presence in 1-1 conversation 2024-07-15 16:10:36 +02:00
Sylvain Berfini
76ba9e5e35 Using recyclerview to improve delivery status list performances 2024-07-15 16:10:36 +02:00
Sylvain Berfini
b9e4fcf1a6 Simple reactions to chat message display 2024-07-15 16:10:36 +02:00
Sylvain Berfini
f821707cc6 More work related to IMDNs display & chat in general 2024-07-15 16:10:36 +02:00
Sylvain Berfini
c151ef1526 Improved delivery bottom sheet 2024-07-15 16:10:36 +02:00
Sylvain Berfini
170e441744 Trying to optimize the ContactAvatarModel use 2024-07-15 16:10:36 +02:00
Sylvain Berfini
be908cdf0e Started proto for IMDNs display 2024-07-15 16:10:36 +02:00
Sylvain Berfini
0defb639c2 Fixed group avatar with two participants 2024-07-15 16:10:36 +02:00
Sylvain Berfini
3d455d0fc9 Finished emoji picker, started file picker 2024-07-15 16:10:36 +02:00
Sylvain Berfini
561c36bfe0 Added conference factory URIs & LIME server URL to account params 2024-07-15 16:10:36 +02:00
Sylvain Berfini
f07a8f6c2b Added chat room participant admin popup to remove a person from a group & give/remove admin rights 2024-07-15 16:10:36 +02:00
Sylvain Berfini
1c7b97d8db Added subject field for group chat room creation 2024-07-15 16:10:36 +02:00
Sylvain Berfini
ee46722a3d Started multiple contact selection in chat room creation 2024-07-15 16:10:36 +02:00
Sylvain Berfini
4fb4c7c85d Fixed manual filter of chat rooms 2024-07-15 16:10:36 +02:00
Sylvain Berfini
8526c24c3e Started 1-1 chat room creation 2024-07-15 16:10:36 +02:00
Sylvain Berfini
db72bffcbd Do not show notifications for messages received in currently displayed chat room 2024-07-15 16:10:36 +02:00
Sylvain Berfini
b3396fa62e Do no apply filter in conversation when filter is empty 2024-07-15 16:10:36 +02:00
Sylvain Berfini
17d3208cbd Fixed assistant pages header on large tablets in landscape 2024-07-15 16:10:36 +02:00
Sylvain Berfini
f9d2e04609 Added search in conversation top bar 2024-07-15 16:10:36 +02:00
Sylvain Berfini
a2355e3225 Started conversation info fragment 2024-07-15 16:10:36 +02:00
Sylvain Berfini
c076dcb2c7 Fixed account profile avatar not being updated when set/removed + started chat event display 2024-07-15 16:10:36 +02:00
Sylvain Berfini
ae1e2599a8 Improved account mode fragment 2024-07-15 16:10:36 +02:00
Sylvain Berfini
8858deb42f Improved incoming bubble layout 2024-07-15 16:10:36 +02:00
Sylvain Berfini
e3c1280278 No longer move chat message position in long press dialog to match position in list + disabled emojis & reply action if chat room is read only 2024-07-15 16:10:36 +02:00
Sylvain Berfini
8794146df7 ShapeImageView can handle rounding images by itself, no need for Coil's CircleCropTransform 2024-07-15 16:10:36 +02:00
Sylvain Berfini
4ce5d989c5 Use ShapableImageView to easily draw borders around avatars, fixed trust badge display condition 2024-07-15 16:10:36 +02:00
Sylvain Berfini
1dc5776cb8 Fixed group avatar with 3 images 2024-07-15 16:10:36 +02:00
Sylvain Berfini
43cf0f5cba Finished removing AvatarView (still borders to do and fix display issue when group has 3 participants) 2024-07-15 16:10:36 +02:00
Sylvain Berfini
e4a3ec37c5 Started removing AvatarView dependency 2024-07-15 16:10:36 +02:00
Sylvain Berfini
2cef7980ea Fixed hidden last message(s) of conversation 2024-07-15 16:10:36 +02:00
Sylvain Berfini
db5ea158c3 Send custom reaction using emoji picker 2024-07-15 16:10:36 +02:00
Sylvain Berfini
daacb3ca98 Huge performances boost by removing AvatarView, todo: do it everywhere 2024-07-15 16:10:36 +02:00
Sylvain Berfini
888c8c453a Reworked algorithm that groups chat messages together 2024-07-15 16:10:36 +02:00
Sylvain Berfini
c9db3df251 Reworked permissions fragment 2024-07-15 16:10:36 +02:00
Sylvain Berfini
faf3e887b9 Added send reaction, copy text & delete options to chat message long press menu 2024-07-15 16:10:36 +02:00
Sylvain Berfini
8269228b8a Update some info dynamically 2024-07-15 16:10:36 +02:00
Sylvain Berfini
d9b6f0482a PoC for blurring conversation except for long pressed chat bubble 2024-07-15 16:10:36 +02:00
Sylvain Berfini
b8d8e877d7 More work on conversation 2024-07-15 16:10:36 +02:00
Sylvain Berfini
db6f71c8cb Added simple text message sending + improved sent chat bubble 2024-07-15 16:10:36 +02:00
Sylvain Berfini
9c6533bceb Added unread messages count indicator to bottom nav bar 2024-07-15 16:10:36 +02:00
Sylvain Berfini
0d43006a14 Added missing paddings to notifications counter to be more centered in background circle 2024-07-15 16:10:36 +02:00
Sylvain Berfini
8a62a68d6e Added group avatar for group chat rooms 2024-07-15 16:10:36 +02:00
Sylvain Berfini
3dc23d906e Improved incoming bubble layout 2024-07-15 16:10:36 +02:00
Sylvain Berfini
9d5474f352 Added popup menu & meeting removal, hiding operation in progress for now 2024-07-15 16:10:36 +02:00
Sylvain Berfini
3ae9740336 Started meeting detail view 2024-07-15 16:10:36 +02:00
Sylvain Berfini
5531dabae4 Started meeting scheduler UI 2024-07-15 16:10:36 +02:00
Sylvain Berfini
7c28c37d0b Improved touch size of some buttons 2024-07-15 16:10:36 +02:00
Sylvain Berfini
b6bdca7b89 Apply filter to meetings list + only show meetings for default account 2024-07-15 16:10:36 +02:00
Sylvain Berfini
c952178749 Started grouping chat messages from same person in a short interval of time 2024-07-15 16:10:36 +02:00
Sylvain Berfini
395dc379ed Fixed issue in conversations list & conversation related to group 2024-07-15 16:10:36 +02:00
Sylvain Berfini
3873028209 Started meetings list 2024-07-15 16:10:36 +02:00
Sylvain Berfini
4a11554fa5 Store latest visited main page & use it as default page on next app opening + fixed registration failure top bar click 2024-07-15 16:10:36 +02:00
Sylvain Berfini
2fb97fbc51 Started displaying messages 2024-07-15 16:10:35 +02:00
Sylvain Berfini
c12ddf43a2 Started conversation layout 2024-07-15 16:10:35 +02:00
Sylvain Berfini
a481b5c6cd Fixed list header when only suggestions 2024-07-15 16:10:35 +02:00
Sylvain Berfini
797418ce1a Started new chat room fragment 2024-07-15 16:10:35 +02:00
Sylvain Berfini
95baa55472 Hidden separators, added red top bar for non-default accout registration failure notification 2024-07-15 16:10:35 +02:00
Sylvain Berfini
b105c436ba Bumped dependencies 2024-07-15 16:10:35 +02:00
Sylvain Berfini
0d880dda50 Started to display conversations list with long press menu 2024-07-15 16:10:35 +02:00
Sylvain Berfini
3f24e73978 Added grant all button on permissions fragment 2024-07-15 16:10:35 +02:00
Sylvain Berfini
7eaa5cfb22 Show persistent red toast while default account is in failed registration state 2024-07-15 16:10:35 +02:00
Sylvain Berfini
a66d29ba2e Showing media encryption icon 2024-07-15 16:10:35 +02:00
Sylvain Berfini
c3de2e906b Updated dependencies, better way to show/hide keyboard 2024-07-15 16:10:35 +02:00
Sylvain Berfini
30d03d280c Updated path to debug APK in CI 2024-07-15 16:10:35 +02:00
Sylvain Berfini
3971b18b04 Removed deprecated code 2024-07-15 16:10:35 +02:00
Sylvain Berfini
46c3ed0b0d Wait a bit before turning status bar green in MainActivity to give time to start CallActivity that will change it back to orange anyway 2024-07-15 16:10:35 +02:00
Sylvain Berfini
bef704d445 Moved account unread notification count in drawer 2024-07-15 16:10:35 +02:00
Sylvain Berfini
6e027811ee Updated default avatar in contact editor + remove it when a picture is selected 2024-07-15 16:10:35 +02:00
Sylvain Berfini
7f9dbaec2a Removed history list swipe to remove callbacks 2024-07-15 16:10:35 +02:00
Sylvain Berfini
3bbccf6c89 Added player for device default ringtone in settings 2024-07-15 16:10:35 +02:00
Sylvain Berfini
528855c5d5 Updated navigation & other views when switching default account 2024-07-15 16:10:35 +02:00
Sylvain Berfini
78dd449baf Do not disable save button in contact editor when mandatory fields aren't filled, TODO: notify user 2024-07-15 16:10:35 +02:00
Sylvain Berfini
ada4e786ec Pre-display the list of calls list after 20 items 2024-07-15 16:10:35 +02:00
Sylvain Berfini
57094d6cce Added CI 2024-07-15 16:10:35 +02:00
Sylvain Berfini
de37ae245d Added account settings 2024-07-15 16:10:35 +02:00
Sylvain Berfini
e1c4be005f Updated SMS invite icon 2024-07-15 16:10:35 +02:00
Sylvain Berfini
df728f6856 Added gradient to assistant & welcome header image 2024-07-15 16:10:35 +02:00
Sylvain Berfini
1445760cc0 Fixed firstName / lastName mixup 2024-07-15 16:10:35 +02:00
Sylvain Berfini
3c0640ce11 Updated remote recording to show toast 2024-07-15 16:10:35 +02:00
Sylvain Berfini
9268ef5d2f Updated some colors 2024-07-15 16:10:35 +02:00
Sylvain Berfini
adad98f3e2 Added permissions fragment 2024-07-15 16:10:35 +02:00
Sylvain Berfini
ea87c48586 Added help popup for international prefix picker in account profile 2024-07-15 16:10:35 +02:00
Sylvain Berfini
9c06c9802d Updated margins 2024-07-15 16:10:35 +02:00
Sylvain Berfini
2686f80502 Change transfer call action label when more than one call 2024-07-15 16:10:35 +02:00
Sylvain Berfini
5e50ef4045 Fixed in-call top bar displaying other calls 2024-07-15 16:10:35 +02:00
Sylvain Berfini
c4fe71c59f Added internation prefix picker in account profile 2024-07-15 16:10:35 +02:00
Sylvain Berfini
860371e698 Added top bar while in call to display paused calls 2024-07-15 16:10:35 +02:00
Sylvain Berfini
1d0abc4cb9 Attended transfer 2024-07-15 16:10:35 +02:00
Sylvain Berfini
d0f052177e Fixed sliding pane layout width on mobiles + pause ringtone player when leaving settings 2024-07-15 16:10:35 +02:00
Sylvain Berfini
429e8d2704 Added ended call fragment 2024-07-15 16:10:35 +02:00
Sylvain Berfini
464865c091 Shorten selected country in account creation spinner 2024-07-15 16:10:35 +02:00
Sylvain Berfini
7f5a9763e7 Fixed order when adding new contact from history 2024-07-15 16:10:35 +02:00
Sylvain Berfini
da707dda4b No longer copy logs URL in clipboard automatically, can be done from sharing screen if needed 2024-07-15 16:10:35 +02:00
Sylvain Berfini
2205ad1eb8 Replaced fat check by simple one in toasts 2024-07-15 16:10:35 +02:00
Sylvain Berfini
db722badaf Disable camera button when call is paused + show Paused / Paused by remote instead of chrono 2024-07-15 16:10:35 +02:00
Sylvain Berfini
e6387e124f Updated welcome pages with proper content 2024-07-15 16:10:35 +02:00
Sylvain Berfini
c72e2cb852 Added pause/resume from in-call extras actions + update available dialog + removed unused resources 2024-07-15 16:10:35 +02:00
Sylvain Berfini
e35b76d0f4 Updated icons according to latest changes 2024-07-15 16:10:35 +02:00
Sylvain Berfini
f00c21be91 Update recording indicator + added remote recording indicator 2024-07-15 16:10:35 +02:00
Sylvain Berfini
62fa5515e1 Updated colors & in-call header 2024-07-15 16:10:35 +02:00
Sylvain Berfini
e6287631aa Added country to prefix picker popup list 2024-07-15 16:10:35 +02:00
Sylvain Berfini
377f5000a5 Added animation + white background to settings page 2024-07-15 16:10:35 +02:00
Sylvain Berfini
82c041329b Added empty account settings fragment 2024-07-15 16:10:35 +02:00
Sylvain Berfini
f1c410deaa Added light/dark/auto theme setting 2024-07-15 16:10:35 +02:00
Sylvain Berfini
429553a6df Added ringtone player next to picker 2024-07-15 16:10:35 +02:00
Sylvain Berfini
2a1b2bf7ac Started ringtone picker 2024-07-15 16:10:35 +02:00
Sylvain Berfini
bdca32be49 Added call recording 2024-07-15 16:10:35 +02:00
Sylvain Berfini
95e8ef9fc4 Finished trust/distrust icons & border color 2024-07-15 16:10:35 +02:00
Sylvain Berfini
a2433956fe Started RecyclerViewSwipe 2024-07-15 16:10:35 +02:00
Sylvain Berfini
1fe8bad37b Moved some functions from LinphoneUtils to AppUtils 2024-07-15 16:10:35 +02:00
Sylvain Berfini
78eec3f6c8 Updated icon for active call in calls list 2024-07-15 16:10:35 +02:00
Sylvain Berfini
6d0886423c Show operation in progress view while loading contacts & call history for the first time 2024-07-15 16:10:35 +02:00
Sylvain Berfini
e4f4396a6b Added QR code remote provisioning 2024-07-15 16:10:35 +02:00
Sylvain Berfini
4336266b7f Disable account creation if push notifications aren't available 2024-07-15 16:10:35 +02:00
Sylvain Berfini
e5617d53ee Reworked & improved how we load contact pictures 2024-07-15 16:10:35 +02:00
Sylvain Berfini
a037fb5487 Updated away color 2024-07-15 16:10:35 +02:00
Sylvain Berfini
80c1b5722f Fixed dropdown overlap 2024-07-15 16:10:35 +02:00
Sylvain Berfini
95401fb8c4 Added edit/remove contact/account picture 2024-07-15 16:10:35 +02:00
Sylvain Berfini
42dd293aa8 Use dropdown for country picker 2024-07-15 16:10:35 +02:00
Sylvain Berfini
f4566ce812 Display currently selected country flag 2024-07-15 16:10:35 +02:00
Sylvain Berfini
cb20acfa52 Display unread messages & missed calls counters on each account in side menu 2024-07-15 16:10:35 +02:00
Sylvain Berfini
565e957387 Fixed issue when creating a new contact 2024-07-15 16:10:35 +02:00
Sylvain Berfini
9fcdb2bffa Made account devices remove button clickable 2024-07-15 16:10:35 +02:00
Sylvain Berfini
1c72196943 Bottom nav bar & corePreferences changes 2024-07-15 16:10:35 +02:00
Sylvain Berfini
845fd7ee03 Reverted scrollview, causing issues with recyclerview 2024-07-15 16:10:35 +02:00
Sylvain Berfini
5dc4c32eba Made history contact fragment scrollable so it is usable in landscape 2024-07-15 16:10:35 +02:00
Sylvain Berfini
d58e5f9fc2 Added contacts list filter popup menu 2024-07-15 16:10:35 +02:00
Sylvain Berfini
027e5dd61b Started blind call transfer 2024-07-15 16:10:35 +02:00
Sylvain Berfini
a60c66ad33 Added pause/resume action to long press menu in calls list + fixing issues with multi calls 2024-07-15 16:10:35 +02:00
Sylvain Berfini
51179c083c Not needed 2024-07-15 16:10:35 +02:00
Sylvain Berfini
ee8779cc00 Added in-call numpad 2024-07-15 16:10:35 +02:00
Sylvain Berfini
913e7cdb02 Fixed call duration timer when leaving & resuming active call fragment 2024-07-15 16:10:35 +02:00
Sylvain Berfini
46e06f2c9d Added long press menu to calls list 2024-07-15 16:10:35 +02:00
Sylvain Berfini
ad06f989b7 Fixed issue with foreground service & notifications 2024-07-15 16:10:35 +02:00
Sylvain Berfini
f5a4922aa3 Renamed VoiP to Call 2024-07-15 16:10:35 +02:00
Sylvain Berfini
856e3542e8 Renamed history related code & layout 2024-07-15 16:10:35 +02:00
Sylvain Berfini
8011bd997c Started calls list 2024-07-15 16:10:35 +02:00
Sylvain Berfini
f345db49cd Updated color + added ZRTP SAS validation in landscape 2024-07-15 16:10:35 +02:00
Sylvain Berfini
2896da7f9d Added back Android auto Manifest info 2024-07-15 16:10:35 +02:00
Sylvain Berfini
c314c4cba3 Updated notifications manager to allow mark as read & reply on chat messages notifications 2024-07-15 16:10:35 +02:00
Sylvain Berfini
686503c83c Improvements on search native contact when UI hasn't been started 2024-07-15 16:10:35 +02:00
Sylvain Berfini
fe3a448231 No longer need to do the iterate ourselves, the SDK can handle it on the proper thread now 2024-07-15 16:10:35 +02:00
Sylvain Berfini
1b2f6f4e3d Updated disabled color in accounts list in drawer menu 2024-07-15 16:10:35 +02:00
Sylvain Berfini
262d6d551b Fixed contact lookup from background 2024-07-15 16:10:35 +02:00
Sylvain Berfini
4d2d01195a Display account display name for which incoming call is being received if more than one account 2024-07-15 16:10:35 +02:00
Sylvain Berfini
a68977d9dd Hide numpad when leaving start call fragment 2024-07-15 16:10:35 +02:00
Sylvain Berfini
952bde0e96 Select TLS as default transport for third party SIP accounts 2024-07-15 16:10:35 +02:00
Sylvain Berfini
93bb1c0c96 Also hide video call button if video is disabled 2024-07-15 16:10:35 +02:00
Sylvain Berfini
544f37a75b Hide message button if chat is disabled 2024-07-15 16:10:35 +02:00
Sylvain Berfini
141c613eb9 Added confirmation dialog for contact removal & call logs removal 2024-07-15 16:10:35 +02:00
Sylvain Berfini
6faa7b4cb6 Added missing confirm dialog when leaving contact edit fragment 2024-07-15 16:10:35 +02:00
Sylvain Berfini
c4f666fdd6 Various changes 2024-07-15 16:10:35 +02:00
Sylvain Berfini
e90aa13890 Added flags in country adapter 2024-07-15 16:10:35 +02:00
Sylvain Berfini
cb6850f287 Improved notifications manager to remove notification when chat room has been read 2024-07-15 16:10:35 +02:00
Sylvain Berfini
58b93a9fa9 Increased back buttons size 2024-07-15 16:10:34 +02:00
Sylvain Berfini
809fd2f7ff Renamed colors 2024-07-15 16:10:34 +02:00
Sylvain Berfini
5813c5d9d8 Added proper error message for failed auth 2024-07-15 16:10:34 +02:00
Sylvain Berfini
62139047b3 Various UI changes 2024-07-15 16:10:34 +02:00
Sylvain Berfini
6974c53194 Fixed account profile bottom margin 2024-07-15 16:10:34 +02:00
Sylvain Berfini
3832299463 More error info in assistant 2024-07-15 16:10:34 +02:00
Sylvain Berfini
8f51de45d0 Updated links 2024-07-15 16:10:34 +02:00
Sylvain Berfini
ea5b9518ad Updated skip button from welcome activity to directly go to Assistant 2024-07-15 16:10:34 +02:00
Sylvain Berfini
d1029af180 Started new call fragment while in call 2024-07-15 16:10:34 +02:00
Sylvain Berfini
88c5e28577 Removed contact_avatar, use user_circle instead 2024-07-15 16:10:34 +02:00
Sylvain Berfini
ce8aee9192 Fixed find contact by phone number 2024-07-15 16:10:34 +02:00
Sylvain Berfini
08643df831 Added translatable strings for account profile devices 2024-07-15 16:10:34 +02:00
Sylvain Berfini
42a115e93b Improved RecyclerViewHeaderDecoration to make it sticky 2024-07-15 16:10:34 +02:00
Sylvain Berfini
9c9391c95b Merged contacts & suggestions in the same list 2024-07-15 16:10:34 +02:00
Sylvain Berfini
82ae513d17 Improved layout for landscape foldables 2024-07-15 16:10:34 +02:00
Sylvain Berfini
c3101e92b4 Created styles to avoid code duplication 2024-07-15 16:10:34 +02:00
Sylvain Berfini
14dca2f984 Improved reaction notification 2024-07-15 16:10:34 +02:00
Sylvain Berfini
d531a47c70 Prevent calls list to scroll to top at each refresh 2024-07-15 16:10:34 +02:00
Sylvain Berfini
47c2024f67 Removed code to alter nav bar color 2024-07-15 16:10:34 +02:00
Sylvain Berfini
5eb323c662 Added in-call top bar 2024-07-15 16:10:34 +02:00
Sylvain Berfini
2181c74b3e Updated signal strength alert due to changes in SDK 2024-07-15 16:10:34 +02:00
Sylvain Berfini
1665c4de22 Started devices in account profile 2024-07-15 16:10:34 +02:00
Sylvain Berfini
62cb1fb3f1 Added confirmation dialog for account removal 2024-07-15 16:10:34 +02:00
Sylvain Berfini
a2bd50ee22 Design changes 2024-07-15 16:10:34 +02:00
Sylvain Berfini
61365ff3c2 Changes related to TODO comments 2024-07-15 16:10:34 +02:00
Sylvain Berfini
86d9e25f17 Differentiate between WiFi and cellular signals in alerts 2024-07-15 16:10:34 +02:00
Sylvain Berfini
e0b146039f Refresh friend after edition + limit native contacts refresh to 1 per minute 2024-07-15 16:10:34 +02:00
Sylvain Berfini
54a299f23c Started settings 2024-07-15 16:10:34 +02:00
Sylvain Berfini
24a5ae8122 Added help layout 2024-07-15 16:10:34 +02:00
Sylvain Berfini
27063c9e45 Added profile mode fragment for account params 2024-07-15 16:10:34 +02:00
Sylvain Berfini
78e40807b1 Finished account profile 2024-07-15 16:10:34 +02:00
Sylvain Berfini
7fef6bde78 Updated account profile fragment, still work to do 2024-07-15 16:10:34 +02:00
Sylvain Berfini
7f78cf23d7 Started account profile mode (secure/default vs interop) 2024-07-15 16:10:34 +02:00
Sylvain Berfini
34424dfbc3 Renamed some drawables, removed some others 2024-07-15 16:10:34 +02:00
Sylvain Berfini
d51f25ea88 Added backspace button in numpad 2024-07-15 16:10:34 +02:00
Sylvain Berfini
c8ea1bcd8c Fixed issue with video call layout + auto accept video requests 2024-07-15 16:10:34 +02:00
Sylvain Berfini
036301e34f Code cleanup 2024-07-15 16:10:34 +02:00
Sylvain Berfini
e3022f42ab Removed legacy chat views 2024-07-15 16:10:34 +02:00
Sylvain Berfini
548a597843 Added missing androidx.media dependency 2024-07-15 16:10:34 +02:00
Sylvain Berfini
f6479826ca Added support of PiP 2024-07-15 16:10:34 +02:00
Sylvain Berfini
e4551713dc Fixed issue with call notifications & channels 2024-07-15 16:10:34 +02:00
Sylvain Berfini
18254fd385 Reworked in-call fragment to properly handle foldable phones 2024-07-15 16:10:34 +02:00
Sylvain Berfini
0dbc9f7a8a Moved strings to translatable file + use only toast layout 2024-07-15 16:10:34 +02:00
Sylvain Berfini
7e36ffc2b4 Make third part ysip account warning scrollable 2024-07-15 16:10:34 +02:00
Sylvain Berfini
16dc339b2d Improved toasts mechanism so they don't overlap 2024-07-15 16:10:34 +02:00
Sylvain Berfini
ae7c35c75d Fixed suggestions list not scrollable 2024-07-15 16:10:34 +02:00
Sylvain Berfini
5845c079cb Simplified generic fragment logic for going back 2024-07-15 16:10:34 +02:00
Sylvain Berfini
b6f8fb3354 Reverting, not really working 2024-07-15 16:10:34 +02:00
Sylvain Berfini
48db3d4aa2 Fixed sliding pane opening on resume issue 2024-07-15 16:10:34 +02:00
Sylvain Berfini
702504e1d5 Added logs & fixed nav graph id 2024-07-15 16:10:34 +02:00
Sylvain Berfini
619ab8a6ed Fixed issue with byNavGraphViewModel 2024-07-15 16:10:34 +02:00
Sylvain Berfini
594bcffd7c Added operation in progress layout 2024-07-15 16:10:34 +02:00
Sylvain Berfini
46dfd0bfa0 Bumped dependencies 2024-07-15 16:10:34 +02:00
Sylvain Berfini
bd523f307b Set +1 prefix by default 2024-07-15 16:10:34 +02:00
Sylvain Berfini
b220d979bd Re-use FlexiAPI token until it is consumed by account creation 2024-07-15 16:10:34 +02:00
Sylvain Berfini
85a904e173 Hide call controls when in full screen 2024-07-15 16:10:34 +02:00
Sylvain Berfini
16a467b7d1 Added country picker for assistant's account register 2024-07-15 16:10:34 +02:00
Sylvain Berfini
f4b6deb06a Layout changes for tablet 2024-07-15 16:10:34 +02:00
Sylvain Berfini
4b6ab58048 Fixes 2024-07-15 16:10:34 +02:00
Sylvain Berfini
b0a1ac3ee4 Fixed caret direction when in-call bottom sheet is opened by dragging the handle 2024-07-15 16:10:34 +02:00
Sylvain Berfini
8e1d22bbb9 Using permanent bottom sheet for in-call extras actions as well 2024-07-15 16:10:34 +02:00
Sylvain Berfini
dc01abf48f Added missing trims 2024-07-15 16:10:34 +02:00
Sylvain Berfini
fa7c6907be Reworked navigation 2024-07-15 16:10:34 +02:00
Sylvain Berfini
cbc46ed2e0 Improved dialer by using permanent bottom sheet 2024-07-15 16:10:34 +02:00
Sylvain Berfini
f0e39e92b7 Fixed issue in number picker 2024-07-15 16:10:34 +02:00
Sylvain Berfini
db3117b92e Compute & show last seen online at + close sip address/phone number picker dialog when used 2024-07-15 16:10:34 +02:00
Sylvain Berfini
3f6339887b Fixed no avatar issue in calls list since it is the default fragment 2024-07-15 16:10:34 +02:00
Sylvain Berfini
8057e9d0af Update friends lists subscription when switching default account 2024-07-15 16:10:34 +02:00
Sylvain Berfini
b08aa2ae1f Added clickable links to conditions & privacy policy dialog 2024-07-15 16:10:34 +02:00
Sylvain Berfini
a7a22f39d2 Show privacy policy & conditions accept dialog if not accepted yet when creating an account or logging in a third party one 2024-07-15 16:10:34 +02:00
Sylvain Berfini
6f416ab33f Set calls as start fragment 2024-07-15 16:10:34 +02:00
Sylvain Berfini
17ef0a5ca7 Improved back nav 2024-07-15 16:10:34 +02:00
Sylvain Berfini
643a2be9a2 Fixed drawer menu accounts list not refreshing avatar 2024-07-15 16:10:34 +02:00
Sylvain Berfini
29a47b87cd Made accounts switch work 2024-07-15 16:10:34 +02:00
Sylvain Berfini
36b5430861 Added third party SIP account login form 2024-07-15 16:10:34 +02:00
Sylvain Berfini
4f03015486 Added third party sip account login warning fragment 2024-07-15 16:10:34 +02:00
Sylvain Berfini
7b48d41d29 Using new APIs 2024-07-15 16:10:34 +02:00
Sylvain Berfini
2fe1bcbdff Started account creation 2024-07-15 16:10:34 +02:00
Sylvain Berfini
489483e22a Add back buttons in new fragments 2024-07-15 16:10:34 +02:00
Sylvain Berfini
89c7e734d4 Changes for friends list storage 2024-07-15 16:10:34 +02:00
Sylvain Berfini
b93f75aade Improved account avatar 2024-07-15 16:10:34 +02:00
Sylvain Berfini
157c233ab1 Fixed issue with abort confirmation dialog in new contact fragment 2024-07-15 16:10:34 +02:00
Sylvain Berfini
72e7445f87 Removed Friend aggregation for suggestions 2024-07-15 16:10:34 +02:00
Sylvain Berfini
ccf7ff82a1 Show SIP address & added international prefix setting in account profile 2024-07-15 16:10:34 +02:00
Sylvain Berfini
8342259054 Bumped min SDK to Android 9.0 2024-07-15 16:10:34 +02:00
Sylvain Berfini
abd5b865e1 Changes for assistant 2024-07-15 16:10:34 +02:00
Sylvain Berfini
973afe08c5 Added numpad 2024-07-15 16:10:34 +02:00
Sylvain Berfini
3201655870 Factorized code to fix issue in sip/number picking in start call fragmet 2024-07-15 16:10:34 +02:00
Sylvain Berfini
c051d28c95 Improvements 2024-07-15 16:10:34 +02:00
Sylvain Berfini
ab151cc409 Fixed performances issue by replacing suggestions recyclerview by linearlayout... 2024-07-15 16:10:34 +02:00
Sylvain Berfini
3c94068910 Layout of start call as intended but perfomances not great... 2024-07-15 16:10:34 +02:00
Sylvain Berfini
544ae39a95 Added clear field button on start call filter 2024-07-15 16:10:34 +02:00
Sylvain Berfini
26c79e6740 Removed TopBarFragment, simply include layout and have the view model inherits TopBarViewModel (now abstract) 2024-07-15 16:10:34 +02:00
Sylvain Berfini
cb48f73fd9 Started contacts filter button 2024-07-15 16:10:34 +02:00
Sylvain Berfini
728fa279dd Added QR code scanner 2024-07-15 16:10:34 +02:00
Sylvain Berfini
3d5ca3313b Do not use CoreService as it will attempt to use the Core on the main thread, TODO : disable activity monitor in SDK 2024-07-15 16:10:34 +02:00
Sylvain Berfini
ca080b2dcc Added underlines 2024-07-15 16:10:34 +02:00
Sylvain Berfini
476eabd0fe Trying to route audio using androidx Telecom API... 2024-07-15 16:10:34 +02:00
Sylvain Berfini
39ad8347c7 Started playing around with alerts 2024-07-15 16:10:34 +02:00
Sylvain Berfini
294410bfd2 Added first start presentation caroussel 2024-07-15 16:10:34 +02:00
Sylvain Berfini
92b3d08ea0 Using new androix telecom library 2024-07-15 16:10:34 +02:00
Sylvain Berfini
91b049e3a7 Using recyclerview for call log details to improve performances 2024-07-15 16:10:34 +02:00
Sylvain Berfini
5af3a490d4 Added contact export as vCard from contacts list 2024-07-15 16:10:34 +02:00
Sylvain Berfini
855b03fb34 Hide 'invite' menu when phone number has presence info 2024-07-15 16:10:34 +02:00
Sylvain Berfini
bb55246197 Fixed contac/call log showing when going back from account profile + added logs 2024-07-15 16:10:33 +02:00
Sylvain Berfini
395e43a3e6 Added decorators on CorePreference 2024-07-15 16:10:33 +02:00
Sylvain Berfini
656377875b Added gradient background to start group call button 2024-07-15 16:10:33 +02:00
Sylvain Berfini
32c5f56be1 Added confirmation dialog when trying to call a particular contact device to increase trust 2024-07-15 16:10:33 +02:00
Sylvain Berfini
ea155cd68c Improved shapes 2024-07-15 16:10:33 +02:00
Sylvain Berfini
7016afa6f8 Added trust explanation dialog in contact detail + improved DialogUtils class 2024-07-15 16:10:33 +02:00
Sylvain Berfini
cc99d4f93f Fixed various issues linked to smart address book 2024-07-15 16:10:33 +02:00
Sylvain Berfini
424d805def Fixed trust icon not visible when leaving & going back into call 2024-07-15 16:10:33 +02:00
Sylvain Berfini
cb9c43c2e7 Simple filter for now on call logs 2024-07-15 16:10:33 +02:00
Sylvain Berfini
ca5648ed03 Added output audio devices selection menu 2024-07-15 16:10:33 +02:00
Sylvain Berfini
e0f6121dc9 Improvements for video calls 2024-07-15 16:10:33 +02:00
Sylvain Berfini
c79f2896da Started audio routes 2024-07-15 16:10:33 +02:00
Sylvain Berfini
a02896333a Changes 2024-07-15 16:10:33 +02:00
Sylvain Berfini
409e658f80 New icons from phosphoricons library + moved blue toast from fragment to activty 2024-07-15 16:10:33 +02:00
Sylvain Berfini
0c8bd49908 Started notifications manager for calls 2024-07-15 16:10:33 +02:00
Sylvain Berfini
5e150ee16a Started missed calls count in bottom nav bar 2024-07-15 16:10:33 +02:00
Sylvain Berfini
09bbe05f08 Started bottom nav bar viewmodel + scroll to top when adding new items to calls history list 2024-07-15 16:10:33 +02:00
Sylvain Berfini
60965b767c Added missing add / go to contact action from calls list context menu + added logs 2024-07-15 16:10:33 +02:00
Sylvain Berfini
64c29d495d Improved switch between contacts & calls lists with postpone at parent level, added animation for account profile fragment 2024-07-15 16:10:33 +02:00
Sylvain Berfini
52238e5d27 Fixed a few things with AvatarView 2024-07-15 16:10:33 +02:00
Sylvain Berfini
1a9e11a258 Fixed show/hide password toggle 2024-07-15 16:10:33 +02:00
Sylvain Berfini
14d1726a7f Disable save contact if no firstname or lastname is set + when adding contact from call history, pre-propulate SIP address 2024-07-15 16:10:33 +02:00
Sylvain Berfini
5ebaf24c04 Disable phone numbers if default account is in secure mode 2024-07-15 16:10:33 +02:00
Sylvain Berfini
8bba5ea2b6 Added thread annotations to constructors + some missing other ones 2024-07-15 16:10:33 +02:00
Sylvain Berfini
9e259f02d8 Improved local friends / avatars / etc... 2024-07-15 16:10:33 +02:00
Sylvain Berfini
ce2a9b0bde Preventing crash due to keyboard visibility listener + improved suggestions list 2024-07-15 16:10:33 +02:00
Sylvain Berfini
bfe56579aa Improved start call lists behavior when doing search 2024-07-15 16:10:33 +02:00
Sylvain Berfini
10bd90ab18 Fixed issue with picture 2024-07-15 16:10:33 +02:00
Sylvain Berfini
cd3a4e0e63 Update local friend & refresh UI after making a change in UI profile 2024-07-15 16:10:33 +02:00
Sylvain Berfini
8b1057b97d Fixed issues with margins and pressed effect 2024-07-15 16:10:33 +02:00
Sylvain Berfini
d915e97ad2 Started account profile view 2024-07-15 16:10:33 +02:00
Sylvain Berfini
64855cba77 Added speaker off/on icons 2024-07-15 16:10:33 +02:00
Sylvain Berfini
9e4c71a3f0 Removed parenthesis after phone number if no label + added missing fragments (empty) 2024-07-15 16:10:33 +02:00
Sylvain Berfini
4c2b67a5aa Added pressed effect on some actions & menus 2024-07-15 16:10:33 +02:00
Sylvain Berfini
bde1258c87 Added user-input as suggestion when starting a new call 2024-07-15 16:10:33 +02:00
Sylvain Berfini
78edc79fc2 Using annotations to check which method is called from which thread 2024-07-15 16:10:33 +02:00
Sylvain Berfini
9ad121f7d7 Started new call fragment 2024-07-15 16:10:33 +02:00
Sylvain Berfini
407e474896 Added back log for which SDK is being used 2024-07-15 16:10:33 +02:00
Sylvain Berfini
c2a74df26d Hiding conversations & meetings for now 2024-07-15 16:10:33 +02:00
Sylvain Berfini
4018155899 Added activity monitor & presence status 2024-07-15 16:10:33 +02:00
Sylvain Berfini
a10f416f15 Added navigation from call log to contact 2024-07-15 16:10:33 +02:00
Sylvain Berfini
4ac78c5b30 Added confirmation dialog when going back during new/edit contact 2024-07-15 16:10:33 +02:00
Sylvain Berfini
838f9f592c Added SMS invite menu & feature 2024-07-15 16:10:33 +02:00
Sylvain Berfini
f13dceaa34 More work on contacts list context menu 2024-07-15 16:10:33 +02:00
Sylvain Berfini
ab15d05ffd Added sharing/removal of contact 2024-07-15 16:10:33 +02:00
Sylvain Berfini
715e6dc8be Fixed focus issue in contact editor 2024-07-15 16:10:33 +02:00
Sylvain Berfini
4a98610b67 Added contact image picker + favourite toggle 2024-07-15 16:10:33 +02:00
Sylvain Berfini
f4a53bee61 Started to allow for SIP address & phone number in new/edit contact form 2024-07-15 16:10:33 +02:00
Sylvain Berfini
cb1774a678 Added back buttons to assistant 2024-07-15 16:10:33 +02:00
Sylvain Berfini
0543ac33d2 Added no calls image & label 2024-07-15 16:10:33 +02:00
Sylvain Berfini
e3b5f0cc77 Various improvements 2024-07-15 16:10:33 +02:00
Sylvain Berfini
74a15cd0a1 Started call history detail popup menu 2024-07-15 16:10:33 +02:00
Sylvain Berfini
7c4b6d5b20 Improved in-call buttons size + toast above video 2024-07-15 16:10:33 +02:00
Sylvain Berfini
627a0d6f9e Added empty start call fragment 2024-07-15 16:10:33 +02:00
Sylvain Berfini
464e8b4899 Fixed duplicated SIP addresses issue 2024-07-15 16:10:33 +02:00
Sylvain Berfini
b2a89a46ca Added delete all call logs menu & dialog 2024-07-15 16:10:33 +02:00
Sylvain Berfini
8fcf3f1baa Added account menu popup 2024-07-15 16:10:33 +02:00
Sylvain Berfini
405ab20ab2 Limit contacts search to linphone contacts 2024-07-15 16:10:33 +02:00
Sylvain Berfini
e5bbe3a553 Reworked presence badge 2024-07-15 16:10:33 +02:00
Sylvain Berfini
5e1c681a8d Started account login & proper display of accounts 2024-07-15 16:10:33 +02:00
Sylvain Berfini
faa4309ece Started contact editor 2024-07-15 16:10:33 +02:00
Sylvain Berfini
79ca1523ef Show SIP address extracted from phone number through smart addressbook 2024-07-15 16:10:33 +02:00
Sylvain Berfini
8be39a6871 Updated list of call log icon depending on status and dir + text color 2024-07-15 16:10:33 +02:00
Sylvain Berfini
cc4bc0c3b0 Edit native contacts in native contact app 2024-07-15 16:10:33 +02:00
Sylvain Berfini
2ef56c0cbb Added delete call log + started top dots menu 2024-07-15 16:10:33 +02:00
Sylvain Berfini
8d74b8f133 Added copy to clipboard in call history list 2024-07-15 16:10:33 +02:00
Sylvain Berfini
8b446e2de0 Improved toast layout 2024-07-15 16:10:33 +02:00
Sylvain Berfini
8882cd9558 Added pressed/disabled effect on some buttons 2024-07-15 16:10:33 +02:00
Sylvain Berfini
174b8923dc Reworked top bar as fragment 2024-07-15 16:10:33 +02:00
Sylvain Berfini
26cd91ceb2 Small fixes 2024-07-15 16:10:33 +02:00
Sylvain Berfini
3a50ab3b91 Added call history in call log details 2024-07-15 16:10:33 +02:00
Sylvain Berfini
0a83695c45 Started call logs 2024-07-15 16:10:33 +02:00
Sylvain Berfini
571fa8c885 Added support to job title 2024-07-15 16:10:33 +02:00
Sylvain Berfini
1901a8535a Started login/create account 2024-07-15 16:10:33 +02:00
Sylvain Berfini
a23ffdcd1f Updated app icon, started drawer menu content 2024-07-15 16:10:33 +02:00
Sylvain Berfini
970652d81f Added video calls 2024-07-15 16:10:33 +02:00
Sylvain Berfini
6124cdd806 Fixed chrono 2024-07-15 16:10:33 +02:00
Sylvain Berfini
97a87c718a Improved toasts 2024-07-15 16:10:33 +02:00
Sylvain Berfini
f368114b88 Added first version of ZRTP SAS confirmation dialog 2024-07-15 16:10:33 +02:00
Sylvain Berfini
c4b0bf0ee0 Started call actions 2024-07-15 16:10:33 +02:00
Sylvain Berfini
fb9acf8da4 Added green & blue toasts + animation (added copy number to clipboard feature) 2024-07-15 16:10:33 +02:00
Sylvain Berfini
2dfc8f930e Updated sizes 2024-07-15 16:10:33 +02:00
Sylvain Berfini
68c132b003 Started outgoing calls 2024-07-15 16:10:33 +02:00
Sylvain Berfini
c2292fdadc Commented out workaround for now 2024-07-15 16:10:33 +02:00
Sylvain Berfini
0e7f00cddd Improved navigation 2024-07-15 16:10:33 +02:00
Sylvain Berfini
0cca9f8152 Updated headers 2024-07-15 16:10:33 +02:00
Sylvain Berfini
d8a6bf08cb Using font and fontWeight in style 2024-07-15 16:10:32 +02:00
Sylvain Berfini
6c8216d360 No need for third partly lib to display devices trust progress 2024-07-15 16:10:32 +02:00
Sylvain Berfini
f365a43f7b Added number/address picker dialog 2024-07-15 16:10:32 +02:00
Sylvain Berfini
af75f4c3a3 Fixed drawer opened to latest seen contact when navigating back 2024-07-15 16:10:32 +02:00
Sylvain Berfini
84e05e9490 Started calls list fragments 2024-07-15 16:10:32 +02:00
Sylvain Berfini
4cc2a11cc5 Updated self avatar with trusted info 2024-07-15 16:10:32 +02:00
Sylvain Berfini
2565d8155d Started devices trust in contact details 2024-07-15 16:10:32 +02:00
Sylvain Berfini
e147efd358 Added long press context menu to phone number & sip address in contact details 2024-07-15 16:10:32 +02:00
Sylvain Berfini
7c0f9585e7 Improved emoji compat use for contacts 2024-07-15 16:10:32 +02:00
Sylvain Berfini
8bb88f397e More work on contact details 2024-07-15 16:10:32 +02:00
Sylvain Berfini
11f4ff4594 Added GenericFragment logic from current app 2024-07-15 16:10:32 +02:00
Sylvain Berfini
29298ef978 Small UI change 2024-07-15 16:10:32 +02:00
Sylvain Berfini
437fa5c128 Added favourites contacts list 2024-07-15 16:10:32 +02:00
Sylvain Berfini
f104d5c891 Moved fragments in dedicated folders + started calls by creating empty files 2024-07-15 16:10:32 +02:00
Sylvain Berfini
5803fe18ed Trying a different architecture to show fragments correctly depending on device size & orientation 2024-07-15 16:10:32 +02:00
Sylvain Berfini
6cccf09bc1 Started favourites 2024-07-15 16:10:32 +02:00
Sylvain Berfini
bb22845b54 Changes to use shared main view model for sliding pane state 2024-07-15 16:10:32 +02:00
Sylvain Berfini
f3233528d6 Reverted some previous changes 2024-07-15 16:10:32 +02:00
Sylvain Berfini
319a971080 Locking orientation once app has started 2024-07-15 16:10:32 +02:00
Sylvain Berfini
0a978f07d9 Started contact details 2024-07-15 16:10:32 +02:00
Sylvain Berfini
4919347095 Started contacts list context menu 2024-07-15 16:10:32 +02:00
Sylvain Berfini
9f943fcaa5 Started contacts list header 2024-07-15 16:10:32 +02:00
Sylvain Berfini
7c8d11ca20 Using new lib to display avatars 2024-07-15 16:10:32 +02:00
Sylvain Berfini
1d9684e11e Started to display contacts 2024-07-15 16:10:32 +02:00
Sylvain Berfini
7e46fb8720 Started slidingpanel 2024-07-15 16:10:32 +02:00
Sylvain Berfini
3bceafef80 Use AppCompatTextView + only one bottom nav bar layout 2024-07-15 16:10:32 +02:00
Sylvain Berfini
5dfd04ad70 Changes 2024-07-15 16:10:32 +02:00
Sylvain Berfini
bd51fe383b Re-organized a few things 2024-07-15 16:10:32 +02:00
Sylvain Berfini
58d2362390 Fixed keyboard & contacts search bar 2024-07-15 16:10:32 +02:00
Sylvain Berfini
254cf3d9cf Open/close keyboard when search bar is made visible/gone 2024-07-15 16:10:32 +02:00
Sylvain Berfini
09bbe650e3 Improved bottom bar layouts 2024-07-15 16:10:32 +02:00
Sylvain Berfini
552459310c Added safe args navigation plugin + using shared element transition to maintain nav bar in place when navigating horizontally but it breaks the horizontal animation, removed them for now 2024-07-15 16:10:32 +02:00
Sylvain Berfini
137dba1bb4 Empty new contact fragment + nav from contacts to chats 2024-07-15 16:10:32 +02:00
Sylvain Berfini
f7f5a3cf50 Started contacts list 2024-07-15 16:10:32 +02:00
Sylvain Berfini
2aeaf330a8 Removed navigation from MainActivity 2024-07-15 16:10:32 +02:00
Sylvain Berfini
bd42eebdcb Do not use by lazy in Datas, will be probably called by UI thread instead of Core thread 2024-07-15 16:10:32 +02:00
Sylvain Berfini
78d55d6c78 Removed hardcoded account, improved things in chat rooms list 2024-07-15 16:10:32 +02:00
Sylvain Berfini
41ea5e4cc7 Moved files around, started bubbles 2024-07-15 16:10:32 +02:00
Sylvain Berfini
33a33867b5 Started chat room layout 2024-07-15 16:10:32 +02:00
Sylvain Berfini
7b71f32d18 Improvements 2024-07-15 16:10:32 +02:00
Sylvain Berfini
c77fae435f Added contacts avatars 2024-07-15 16:10:32 +02:00
Sylvain Berfini
369a6f1977 Finally found a way to do proper animation 2024-07-15 16:10:32 +02:00
Sylvain Berfini
d5c78f58c0 Started new conversation fragment 2024-07-15 16:10:32 +02:00
Sylvain Berfini
a2d038eb46 Fixes & improvements 2024-07-15 16:10:32 +02:00
Sylvain Berfini
09ca9b5351 Added menu 2024-07-15 16:10:32 +02:00
Sylvain Berfini
c0169e7bcb Initial 6.0.0 commit 2024-07-15 16:07:21 +02:00
Sylvain Berfini
418f9ba4c9 6.0.0 cleanup 2024-07-15 16:07:16 +02:00
Sylvain Berfini
c627849382 Added GitHub config file for issues 2024-07-11 16:49:48 +02:00
Sylvain Berfini
0c57a145b8 Updated CHANGELOG from release/5.2 branch 2024-06-17 09:13:17 +02:00
Sylvain Berfini
852e78c5a6 Fixed build with 5.4 SDK since conferenceAddress can now be null 2024-05-23 10:54:01 +02:00
Sylvain Berfini
dad4af5c34 Fixed issue when parsing SIP URI from native contact due to international prefix being applied 2024-03-20 15:54:19 +01:00
Sylvain Berfini
ff683fa361 Forgot in previous commit to check auto accept video policy 2024-03-20 15:54:15 +01:00
Sylvain Berfini
29a4607bdc Show camera icon instead of phone icon when incoming call is video 2024-03-20 15:54:11 +01:00
Sylvain Berfini
3b12128953 Fixed active speaker video hidden when alone in a conference and then someone else joins 2024-03-11 11:46:25 +01:00
Sylvain Berfini
06750ad9ae Fixes for broadcast mode 2024-03-07 15:34:02 +01:00
Sylvain Berfini
8968c899e0 Updated CHANGELOG with minor releases from release/5.2 branch 2024-02-26 15:38:33 +01:00
Sylvain Berfini
4624d2698d Fixed build due to API change in SDK 2024-02-04 11:43:47 +01:00
Sylvain Berfini
c6126f3e9c Catch exception when navigating to account settings 2024-01-24 08:42:04 +01:00
Sylvain Berfini
d8ebc1003a Bumped version name to 5.3.0 & use SDK 5.4 2024-01-18 16:08:57 +01:00
Sylvain Berfini
4da3c2f6fc Fixed merging calls into local conference 2024-01-18 16:07:44 +01:00
Simona Iacob
de2ee00957 Translated using Weblate (Romanian)
Currently translated at 13.8% (116 of 840 strings)

Translation: Linphone/Linphone Android (5.2)
Translate-URL: https://weblate.linphone.org/projects/linphone/linphone-android/ro/
2024-01-18 16:07:36 +01:00
Simona Iacob
5b8c42b418 Added translation using Weblate (Romanian) 2024-01-18 16:07:25 +01:00
Sylvain Berfini
187fff9311 Fixed crash when Service is started before CoreContext 2024-01-18 16:07:16 +01:00
Sylvain Berfini
4ebcfb9b09 Bumped version code for 5.2.0 release + updated CHANGELOG & README 2023-12-21 08:57:14 +01:00
Sylvain Berfini
01b622decc Fixed onNotifyReceived callback prototype, a parameter is now nullable (SDK change) 2023-12-20 08:51:45 +01:00
Sylvain Berfini
b438090092 Removed from AudioRouteUtils code that was changing microphone sound device when changing the playback sound card, SDK is doing that automatically now 2023-12-20 08:50:28 +01:00
Sylvain Berfini
e1714934b4 Prevent crash when file transfer has no name nor file path 2023-12-20 08:49:23 +01:00
Sylvain Berfini
f27da5c95d Fixed microphone & camera while app is in background on Android 14 if keep app alive setting is enabled 2023-12-18 11:12:15 +01:00
Sylvain Berfini
70803664f9 Prevent auth dialog for removed accounts 2023-12-15 10:52:42 +01:00
Sylvain Berfini
93ac3f8c41 Hide switch camera button if video call is receive only 2023-12-15 09:34:38 +01:00
Sylvain Berfini
18756a840d Fixed participants list in expanded meeting list cell 2023-12-14 10:20:28 +01:00
Sylvain Berfini
7386e12e6d Bumped version code for next beta 2023-12-13 09:19:12 +01:00
Sylvain Berfini
04dcbd6075 Fixed broken active speaker video if when joining conference speaking participants info wasn't available 2023-12-12 16:26:59 +00:00
Sylvain Berfini
1e69aa227b Removed beta setting for conference broadcast mode 2023-12-12 16:06:12 +01:00
Sylvain Berfini
d9984f39ba Do not start foreground service with microphone & camera types if call is not active yet 2023-12-12 14:06:50 +01:00
Sylvain Berfini
8ad466e091 Fixed potential deadlock 2023-12-12 12:42:18 +01:00
Sylvain Berfini
4a4ce57864 No longer require STUN server to be set to enable ICE 2023-12-12 10:38:47 +01:00
Sylvain Berfini
11c33bb1b0 Fixed proximity sensor not turning screen off during outgoing call 2023-12-11 16:53:56 +01:00
Sylvain Berfini
047665afac Bumped version code for new beta 2023-12-11 16:30:36 +01:00
Mauricio Campo
dc54cef126 Translated using Weblate (Spanish)
Currently translated at 70.5% (593 of 840 strings)

Translation: Linphone/Linphone Android (5.2)
Translate-URL: https://weblate.linphone.org/projects/linphone/linphone-android/es/
2023-12-11 13:15:38 +01:00
Aliji Emmanuel
e2483037ef Added translation using Weblate (Yoruba) 2023-12-11 13:15:38 +01:00
Sylvain Berfini
586a3972af Removed BLUETOOTH_CONNECT permission, using device added/removed callback instead 2023-12-11 11:28:52 +01:00
Sylvain Berfini
61eb7276a6 Fixed participant cell background color in dark mode while scheduling a conference 2023-11-28 10:05:41 +01:00
Sylvain Berfini
327428d253 Updated version code/name & CHANGELOG, bumped dependencies 2023-11-27 10:07:35 +01:00
Malte Finsterwalder
e76976b2f3 Translated using Weblate (German)
Currently translated at 37.3% (314 of 840 strings)

Translation: Linphone/Linphone Android (master)
Translate-URL: https://weblate.linphone.org/projects/linphone/linphone-android/de/
2023-11-23 21:59:30 +01:00
Oskars G
1730404436 Translated using Weblate (Latvian)
Currently translated at 4.0% (34 of 840 strings)

Translation: Linphone/Linphone Android (master)
Translate-URL: https://weblate.linphone.org/projects/linphone/linphone-android/lv/
2023-10-31 10:46:04 +01:00
Oskars G
f273471a0b Added translation using Weblate (Latvian) 2023-10-31 10:46:04 +01:00
Sylvain Berfini
cff5cb1570 Update contacts list when fragment resumes 2023-10-31 10:24:10 +01:00
Sylvain Berfini
1d09151a2d Fixed account prefix applied to contact even if use prefix for chat and calls is toggled off 2023-10-31 10:24:10 +01:00
Peter Chen
662d53812b Translated using Weblate (Chinese (Traditional))
Currently translated at 100.0% (840 of 840 strings)

Translation: Linphone/Linphone Android (master)
Translate-URL: https://weblate.linphone.org/projects/linphone/linphone-android/zh_Hant/
2023-10-19 05:30:17 +02:00
Libor Filípek
1d74f1d78f Translated using Weblate (Czech)
Currently translated at 100.0% (840 of 840 strings)

Translation: Linphone/Linphone Android (master)
Translate-URL: https://weblate.linphone.org/projects/linphone/linphone-android/cs/
2023-10-19 05:30:17 +02:00
Vassilios Dimitriadis
f89fb41ebb Added translation using Weblate (Greek) 2023-10-19 05:30:17 +02:00
Sylvain Berfini
52b5a81c26 Fixed sent content type 2023-10-18 15:43:24 +02:00
Sylvain Berfini
02501f0804 Enable Auto video codec priority policy in 5.2 migration 2023-10-18 12:03:49 +02:00
Sylvain Berfini
4b27d1180a Added broadcast mode for scheduled conferences 2023-10-04 15:14:48 +02:00
Andrea Gianarda
2334a0e6a8 Use new API to toggle microphone mute capability during a conference. 2023-10-02 12:03:08 +02:00
Sylvain Berfini
c443eefa20 Fixed can't call right after chat room creation 2023-10-02 09:02:11 +02:00
Sylvain Berfini
c1b7cba559 Updated CHANGELOG 2023-09-27 10:00:51 +02:00
Sylvain Berfini
ee13e451d7 Properly display registration status in top bar when it's refreshing 2023-09-27 09:56:47 +02:00
Sylvain Berfini
1f5e3ba646 Fixed XML entities 2023-09-25 17:00:35 +02:00
Sergio Raneli
421dd0d05c Translated using Weblate (Italian)
Currently translated at 100.0% (819 of 819 strings)

Translation: Linphone/Linphone Android (master)
Translate-URL: https://weblate.linphone.org/projects/linphone/linphone-android/it/
2023-09-25 16:56:42 +02:00
Sergio Raneli
3d7692ded3 Translated using Weblate (Italian)
Currently translated at 100.0% (819 of 819 strings)

Translation: Linphone/Linphone Android (master)
Translate-URL: https://weblate.linphone.org/projects/linphone/linphone-android/it/
2023-09-25 16:56:42 +02:00
Stanislav Bukovetskiy
aca2692602 Translated using Weblate (Russian)
Currently translated at 99.5% (815 of 819 strings)

Translation: Linphone/Linphone Android (master)
Translate-URL: https://weblate.linphone.org/projects/linphone/linphone-android/ru/
2023-09-25 16:56:42 +02:00
Sylvain Berfini
ddcfa242fc Trying to improve incoming call screen visibility when device is locked 2023-09-25 15:48:52 +02:00
Sylvain Berfini
390358cc21 Fixed meetings filter colored icon when selected in history 2023-09-25 15:47:44 +02:00
Sylvain Berfini
ce9335f0b0 Handling HearingAid audio devices as Bluetooth 2023-09-25 14:34:47 +02:00
Sylvain Berfini
318ab35eab Another improvement for chat message reaction notification: remove previous notification when user changes it's reaction 2023-09-15 09:55:03 +02:00
Sylvain Berfini
7deef26f80 Fixed navigation issue 2023-09-15 09:46:02 +02:00
Sylvain Berfini
35729e5726 Removed reaction from notification if user remove it's reaction 2023-09-14 17:17:19 +02:00
Sylvain Berfini
ae92b030c5 Prevent multiple auth dialog being stacked 2023-09-13 10:32:59 +02:00
Sylvain Berfini
abc1891694 Prevents duplicates in chat room creation search results 2023-09-11 16:37:45 +02:00
Sylvain Berfini
066179a2ff Bumped dependencies 2023-09-11 16:32:01 +02:00
Sylvain Berfini
0e03420bd2 Show our reaction if any in long press menu + remove it if selected again 2023-09-08 16:28:52 +02:00
Sylvain Berfini
928a4f129b Fixed crash when rotating device while message's reactions bottom sheet is visible 2023-09-07 11:31:45 +02:00
Sylvain Berfini
0deb4d5c11 Fixed scrolling issue in reactions bottom sheet 2023-09-07 11:19:30 +02:00
Sylvain Berfini
4fd1241589 Using new chatRoom muted API from SDK 2023-09-06 12:04:51 +02:00
Sylvain Berfini
c1db11beaf Fixes from crash reported on Play Store 2023-09-06 11:36:14 +02:00
Sylvain Berfini
1e552a94bf Improved display of emoji reactions above chat messages 2023-09-05 17:13:00 +02:00
Sylvain Berfini
555e2a1330 Fixed merge conflicts 2023-09-04 11:21:21 +02:00
NPL
fd2d9d1226 Translated using Weblate (Japanese)
Currently translated at 98.7% (800 of 810 strings)

Translation: Linphone/Linphone Android (master)
Translate-URL: https://weblate.linphone.org/projects/linphone/linphone-android/ja/
2023-09-01 17:28:28 +02:00
Stanislav Bukovetskiy
38d9e09c0e Translated using Weblate (Russian)
Currently translated at 98.8% (801 of 810 strings)

Translation: Linphone/Linphone Android (master)
Translate-URL: https://weblate.linphone.org/projects/linphone/linphone-android/ru/
2023-08-26 05:30:08 +02:00
Andras Sohar
ac19cee575 Translated using Weblate (Hungarian)
Currently translated at 76.7% (622 of 810 strings)

Translation: Linphone/Linphone Android (master)
Translate-URL: https://weblate.linphone.org/projects/linphone/linphone-android/hu/
2023-08-26 05:30:08 +02:00
Tomasz Gleń
8ab7f7474c Translated using Weblate (Polish)
Currently translated at 5.0% (41 of 810 strings)

Translation: Linphone/Linphone Android (master)
Translate-URL: https://weblate.linphone.org/projects/linphone/linphone-android/pl/
2023-08-26 05:30:08 +02:00
Libor Filípek
9310c13c01 Translated using Weblate (Czech)
Currently translated at 100.0% (810 of 810 strings)

Translation: Linphone/Linphone Android (master)
Translate-URL: https://weblate.linphone.org/projects/linphone/linphone-android/cs/
2023-08-26 05:30:08 +02:00
Libor Filípek
ec509410e5 Translated using Weblate (Czech)
Currently translated at 99.8% (809 of 810 strings)

Translation: Linphone/Linphone Android (master)
Translate-URL: https://weblate.linphone.org/projects/linphone/linphone-android/cs/
2023-08-26 05:30:07 +02:00
Peter Chen
5af39cdd09 Translated using Weblate (Chinese (Traditional))
Currently translated at 99.8% (809 of 810 strings)

Translation: Linphone/Linphone Android (master)
Translate-URL: https://weblate.linphone.org/projects/linphone/linphone-android/zh_Hant/
2023-08-26 05:30:07 +02:00
Sylvain Berfini
4dfc21f4b5 Fixed some color issue with emoji reactions + only display counter if more than one same emoji 2023-08-25 16:31:47 +02:00
Sylvain Berfini
ede66c2fa7 Merge branch 'release/5.1' 2023-08-21 12:56:48 +02:00
Sylvain Berfini
cfe04dea19 Set correct version code for 5.1.0 release + updated CHANGELOG 2023-08-21 11:14:39 +02:00
Sylvain Berfini
91554f99ff Bumped version code for public beta 2023-08-07 16:29:55 +02:00
Sylvain Berfini
9e4d75f7f4 Fixed XML entities in translations 2023-08-07 16:21:30 +02:00
NPL
248ae23997 Translated using Weblate (Japanese)
Currently translated at 99.1% (801 of 808 strings)

Translation: Linphone/Linphone Android (5.1 release)
Translate-URL: https://weblate.linphone.org/projects/linphone/linphone-android-5-1-release/ja/
2023-08-07 16:19:17 +02:00
Peter Chen
ccd2ce915e Translated using Weblate (Chinese (Traditional))
Currently translated at 100.0% (808 of 808 strings)

Translation: Linphone/Linphone Android (5.1 release)
Translate-URL: https://weblate.linphone.org/projects/linphone/linphone-android-5-1-release/zh_Hant/
2023-08-07 16:19:17 +02:00
Stanislav Bukovetskiy
c187643eb1 Translated using Weblate (Russian)
Currently translated at 100.0% (808 of 808 strings)

Translation: Linphone/Linphone Android (5.1 release)
Translate-URL: https://weblate.linphone.org/projects/linphone/linphone-android-5-1-release/ru/
2023-08-07 16:19:17 +02:00
Libor Filípek
1ca4440921 Translated using Weblate (Czech)
Currently translated at 100.0% (808 of 808 strings)

Translation: Linphone/Linphone Android (5.1 release)
Translate-URL: https://weblate.linphone.org/projects/linphone/linphone-android-5-1-release/cs/
2023-08-07 16:19:17 +02:00
Peter Chen
3c2aea7f36 Translated using Weblate (Chinese (Traditional))
Currently translated at 100.0% (805 of 805 strings)

Translation: Linphone/Linphone Android (5.1 release)
Translate-URL: https://weblate.linphone.org/projects/linphone/linphone-android-5-1-release/zh_Hant/
2023-08-07 16:19:17 +02:00
Libor Filípek
9b121a026d Translated using Weblate (Czech)
Currently translated at 100.0% (805 of 805 strings)

Translation: Linphone/Linphone Android (5.1 release)
Translate-URL: https://weblate.linphone.org/projects/linphone/linphone-android-5-1-release/cs/
2023-08-07 16:19:17 +02:00
Sylvain Berfini
f1cfa57f42 Fixed call logs details duplicated each time they are displayed 2023-08-07 16:03:15 +02:00
Sylvain Berfini
839efbdb7c Fixed wrong missing incoming call status 2023-08-07 15:28:43 +02:00
Sylvain Berfini
023f29cc59 Fix escaped arobase when starting Linphone by clicking on google's contacts' app SIP field 2023-08-07 11:02:32 +02:00
Sylvain Berfini
c7bb59d991 Prevent some issues related to ZRTP SAS confirmation dialog 2023-08-02 10:33:01 +02:00
Sylvain Berfini
d4d95b7835 Prevent malicious app that could return private file from another package when sharing file in chat 2023-08-01 17:50:54 +02:00
Sylvain Berfini
546db7355b Small change to see if it improves things on some devices 2023-07-31 10:22:34 +02:00
Sylvain Berfini
256a3ed77a Showing confirmation dialog when about to create a new contact without SIP URI nor phone number (won't be displayed) 2023-07-31 10:11:25 +02:00
Sylvain Berfini
08575dd4d1 Bumped dependencies + updated build.gradle a bit 2023-07-31 09:34:16 +02:00
Sylvain Berfini
ed851b76ba Fixed oval background behind avatar in some phones while in landscape 2023-07-27 13:28:05 +02:00
Sylvain Berfini
ed009c9e39 Fixed operation in progress infinit spinner when scheduling a conf without sending invitation by chat 2023-07-27 13:13:22 +02:00
Sylvain Berfini
50178d40de Bumped version code for public beta 2023-07-20 13:39:29 +02:00
Sylvain Berfini
35944bb3a1 Improved check on prefix in assitant + fixed some logs missing assitant tag 2023-07-20 13:27:11 +02:00
Sylvain Berfini
7e694af88f Fixed translation issue + updated locales list 2023-07-20 11:43:53 +02:00
Stanislav Bukovetskiy
44ef7aa142 Translated using Weblate (Russian)
Currently translated at 100.0% (804 of 804 strings)

Translation: Linphone/Linphone Android (5.1 release)
Translate-URL: https://weblate.linphone.org/projects/linphone/linphone-android-5-1-release/ru/
2023-07-19 05:30:24 +02:00
Stanislav Bukovetskiy
8b56d6137d Translated using Weblate (Russian)
Currently translated at 100.0% (804 of 804 strings)

Translation: Linphone/Linphone Android (5.1 release)
Translate-URL: https://weblate.linphone.org/projects/linphone/linphone-android-5-1-release/ru/
2023-07-19 05:30:24 +02:00
Sylvain Berfini
0ee446ffce Update country name when prefix is manually changed 2023-07-17 17:31:58 +02:00
Sylvain Berfini
d623b597b5 Renamed variable to increase code readability 2023-07-11 15:17:14 +02:00
Sylvain Berfini
794233c25a Bumped version code 2023-07-10 13:10:44 +02:00
Rob Vermeulen=
1c28b688c9 Translated using Weblate (Dutch)
Currently translated at 20.5% (165 of 804 strings)

Translation: Linphone/Linphone Android (5.1 release)
Translate-URL: https://weblate.linphone.org/projects/linphone/linphone-android-5-1-release/nl/
2023-07-10 12:51:44 +02:00
Andras Sohar
20ac33a22e Translated using Weblate (Hungarian)
Currently translated at 76.7% (617 of 804 strings)

Translation: Linphone/Linphone Android (5.1 release)
Translate-URL: https://weblate.linphone.org/projects/linphone/linphone-android-5-1-release/hu/
2023-07-10 12:51:41 +02:00
Sylvain Berfini
926413992b Fixed crash when device boots on Android 14 2023-07-10 12:31:30 +02:00
Sylvain Berfini
5ee9178416 Forgot to hide chat button from call menu when disable_chat is set 2023-07-03 18:11:01 +02:00
Sylvain Berfini
6765505ff0 Fixed ANR when opening a PDF inside linphone (VFS enabled for example) 2023-06-28 17:32:18 +02:00
Sylvain Berfini
7335360c26 Prevent chat message notification to be updated for nothing 2023-06-28 17:31:57 +02:00
Sylvain Berfini
04bfce0406 Updated CHANGELOG 2023-06-28 14:45:46 +02:00
Sylvain Berfini
c4eb94a9bf Fixed padding issue in multi line text view in chat 2023-06-26 15:10:00 +02:00
Sylvain Berfini
226095bec7 Disable attach file & voice messages in chat if no file transfer server URL is set 2023-06-20 15:12:12 +02:00
Sylvain Berfini
197ceb9e69 Fixed &appName; XML entity 2023-06-20 10:34:46 +02:00
Tomasz Gleń
b03ef2d42f Translated using Weblate (Polish)
Currently translated at 4.3% (35 of 804 strings)

Translation: Linphone/Linphone Android (5.1 release)
Translate-URL: https://weblate.linphone.org/projects/linphone/linphone-android-5-1-release/pl/
2023-06-18 13:05:46 +02:00
2dengine LLC
e5b0005931 Translated using Weblate (Bulgarian)
Currently translated at 100.0% (804 of 804 strings)

Translation: Linphone/Linphone Android (5.1 release)
Translate-URL: https://weblate.linphone.org/projects/linphone/linphone-android-5-1-release/bg/
2023-06-17 05:30:13 +02:00
Peter Chen
414f5451f5 Translated using Weblate (Chinese (Traditional))
Currently translated at 100.0% (804 of 804 strings)

Translation: Linphone/Linphone Android (5.1 release)
Translate-URL: https://weblate.linphone.org/projects/linphone/linphone-android-5-1-release/zh_Hant/
2023-06-17 05:30:13 +02:00
Libor Filípek
db384d9680 Translated using Weblate (Czech)
Currently translated at 100.0% (804 of 804 strings)

Translation: Linphone/Linphone Android (5.1 release)
Translate-URL: https://weblate.linphone.org/projects/linphone/linphone-android-5-1-release/cs/
2023-06-17 05:30:13 +02:00
2dengine LLC
624ebe1607 Added translation using Weblate (Bulgarian) 2023-06-17 05:30:13 +02:00
Sylvain Berfini
72b92408a1 Specify service type in startForeground for Android 14 2023-06-16 13:39:10 +02:00
Sylvain Berfini
32c8a098c5 Fixed keyboard visibility listener for Android 14 2023-06-15 15:52:37 +02:00
Sylvain Berfini
53e9d92ea4 Hide emoji picker toggle from chat message sending bar when a non-emoji character has been typed 2023-06-15 11:34:20 +02:00
Sylvain Berfini
747f5288b8 Fixed camera switch button visible in PiP 2023-06-15 10:37:12 +02:00
Sylvain Berfini
7519c69de1 Fixed issue where we could return on conference waiting room after hanging up a conference call 2023-06-15 10:28:17 +02:00
Sylvain Berfini
463ade0ade Reset FPS & bandwidth limits when switching video profile from custom to another one 2023-06-13 10:10:24 +02:00
Sylvain Berfini
32f4307674 Using new PushService added in SDK 2023-06-09 09:39:24 +02:00
Sylvain Berfini
4e396e990d Added compat methods to check & request full screen intent permission + other required changes for Android 14 2023-06-08 13:12:15 +02:00
Sylvain Berfini
a19264a2bd Bumped dependencies & targetting Android 14 (API level 34) 2023-06-08 11:38:21 +02:00
Sylvain Berfini
f6938eba52 Removed empty string 2023-06-07 10:37:14 +02:00
Libor Filípek
9294fa9b8e Translated using Weblate (Czech)
Currently translated at 100.0% (803 of 803 strings)

Translation: Linphone/Linphone Android (5.1 release)
Translate-URL: https://weblate.linphone.org/projects/linphone/linphone-android-5-1-release/cs/
2023-06-07 10:32:10 +02:00
Libor Filípek
49992690a0 Translated using Weblate (Czech)
Currently translated at 99.8% (802 of 803 strings)

Translation: Linphone/Linphone Android (5.1 release)
Translate-URL: https://weblate.linphone.org/projects/linphone/linphone-android-5-1-release/cs/
2023-06-07 10:32:10 +02:00
Peter Chen
3d5fdb66d0 Translated using Weblate (Chinese (Traditional))
Currently translated at 99.8% (802 of 803 strings)

Translation: Linphone/Linphone Android (5.1 release)
Translate-URL: https://weblate.linphone.org/projects/linphone/linphone-android-5-1-release/zh_Hant/
2023-06-07 10:32:10 +02:00
Sylvain Berfini
b0cd5524dd Prevent null auth info request dialog 2023-06-07 10:18:32 +02:00
Sylvain Berfini
8df1d2218d Fixed auth info requested dialog showing up after wrong password input in assistant + fixed default account not set in same scenario 2023-06-06 11:48:16 +02:00
Sylvain Berfini
ba1708beaa Prevent play button displayed above videos to be displayed above images as well 2023-06-05 15:55:27 +02:00
Sylvain Berfini
29e8542803 Fixed missing click listener on video file in chat message when preview failed 2023-05-25 11:14:14 +02:00
Sylvain Berfini
83dc8a7b13 Improved MIME type related code 2023-05-25 11:10:24 +02:00
Sylvain Berfini
855f8c11c1 Bumped dependencies 2023-05-25 10:46:00 +02:00
Sylvain Berfini
fc7a17d5e2 Various improvements 2023-05-23 16:43:34 +02:00
Sylvain Berfini
09f5f4f254 Fixed chat message attachment when failing to get video preview picture to display 2023-05-23 13:54:21 +02:00
Sylvain Berfini
e841a8fbc8 Fixed long-term presence icon not refreshed in contacts list 2023-05-23 11:55:45 +02:00
Sylvain Berfini
6dba85d41b Updated changelog with 5.0.12 release info 2023-05-23 11:44:17 +02:00
NPL
e907dbaaca Translated using Weblate (Japanese)
Currently translated at 99.1% (795 of 802 strings)

Translation: Linphone/Linphone Android (5.1 release)
Translate-URL: https://weblate.linphone.org/projects/linphone/linphone-android-5-1-release/ja/
2023-05-22 11:46:33 +02:00
Peter Chen
949d2ca917 Translated using Weblate (Chinese (Traditional))
Currently translated at 100.0% (802 of 802 strings)

Translation: Linphone/Linphone Android (5.1 release)
Translate-URL: https://weblate.linphone.org/projects/linphone/linphone-android-5-1-release/zh_Hant/
2023-05-22 11:46:33 +02:00
Sylvain Berfini
41f6139137 Improved error log in case it is 'expected' 2023-05-22 09:41:23 +02:00
Peter Chen
8050194502 Translated using Weblate (Chinese (Traditional))
Currently translated at 99.8% (809 of 810 strings)

Translation: Linphone/Linphone Android (master)
Translate-URL: https://weblate.linphone.org/projects/linphone/linphone-android/zh_Hant/
2023-05-17 05:30:06 +02:00
NPL
b6f2418a71 Translated using Weblate (Japanese)
Currently translated at 98.8% (790 of 799 strings)

Translation: Linphone/Linphone Android (master)
Translate-URL: https://weblate.linphone.org/projects/linphone/linphone-android/ja/
2023-05-17 05:30:06 +02:00
Sylvain Berfini
666a4f1ad0 Improved how TextureViews are handled in calls & confs 2023-05-16 15:00:35 +02:00
Sylvain Berfini
76334e03e6 Changes for how we handle presence in background + updated presence setting & moved it from Accounts to Contacts 2023-05-16 10:35:27 +02:00
Sylvain Berfini
f748424943 Emoji reactions: improved dialog UI & fixed count 2023-05-16 09:39:53 +02:00
Sylvain Berfini
8249f2c3a6 Using RFC 9078 feature from SDK: IM reactions 2023-05-15 13:51:32 +02:00
Sylvain Berfini
8397debb00 Prevent crash if NotificationManager throws an exception 2023-05-11 16:55:55 +02:00
Sylvain Berfini
d14a62730a Bumped dependencies 2023-05-11 10:43:38 +02:00
Sylvain Berfini
1f9a55699c Fixed video preview not moving if call was started in audio only 2023-05-09 15:11:32 +02:00
Sylvain Berfini
918b0d678d Show authentication requested dialog if needed 2023-05-09 11:26:08 +02:00
Sylvain Berfini
684f913254 Updated CHANGELOG from release/5.0 branch 2023-05-09 10:40:37 +02:00
Sylvain Berfini
27c2b288f6 Fixed xml entity translation issue + removed translated string no longer existing 2023-05-09 09:11:27 +02:00
Sylvain Berfini
bfcdf19869 Fixed xml entity translation issue + removed translated string no longer existing 2023-05-09 09:10:51 +02:00
Peter Chen
f31b3daeac Translated using Weblate (Chinese (Traditional))
Currently translated at 99.7% (797 of 799 strings)

Translation: Linphone/Linphone Android (master)
Translate-URL: https://weblate.linphone.org/projects/linphone/linphone-android/zh_Hant/
2023-05-09 09:08:12 +02:00
Peter Chen
4e613d0d1e Translated using Weblate (Chinese (Traditional))
Currently translated at 99.8% (795 of 796 strings)

Translation: Linphone/Linphone Android (master)
Translate-URL: https://weblate.linphone.org/projects/linphone/linphone-android/zh_Hant/
2023-05-09 09:08:06 +02:00
Tomas Ivanek
8cf2e96abb Translated using Weblate (Czech)
Currently translated at 100.0% (794 of 794 strings)

Translation: Linphone/Linphone Android (master)
Translate-URL: https://weblate.linphone.org/projects/linphone/linphone-android/cs/
2023-05-09 09:08:00 +02:00
Tomas Ivanek
35151ad83f Translated using Weblate (Czech)
Currently translated at 100.0% (794 of 794 strings)

Translation: Linphone/Linphone Android (master)
Translate-URL: https://weblate.linphone.org/projects/linphone/linphone-android/cs/
2023-05-09 09:07:55 +02:00
Peter Chen
c3be132dff Translated using Weblate (Chinese (Traditional))
Currently translated at 100.0% (794 of 794 strings)

Translation: Linphone/Linphone Android (master)
Translate-URL: https://weblate.linphone.org/projects/linphone/linphone-android/zh_Hant/
2023-05-09 09:07:49 +02:00
NPL
17d9237a7a Translated using Weblate (Japanese)
Currently translated at 99.2% (787 of 793 strings)

Translation: Linphone/Linphone Android (master)
Translate-URL: https://weblate.linphone.org/projects/linphone/linphone-android/ja/
2023-05-09 09:07:44 +02:00
Peter Chen
60af85a380 Translated using Weblate (Chinese (Traditional))
Currently translated at 100.0% (793 of 793 strings)

Translation: Linphone/Linphone Android (master)
Translate-URL: https://weblate.linphone.org/projects/linphone/linphone-android/zh_Hant/
2023-05-09 09:07:38 +02:00
Tomas Ivanek
04e7533687 Translated using Weblate (Czech)
Currently translated at 100.0% (793 of 793 strings)

Translation: Linphone/Linphone Android (master)
Translate-URL: https://weblate.linphone.org/projects/linphone/linphone-android/cs/
2023-05-09 09:07:32 +02:00
Peter Chen
05d76898dc Translated using Weblate (Chinese (Traditional))
Currently translated at 100.0% (793 of 793 strings)

Translation: Linphone/Linphone Android (master)
Translate-URL: https://weblate.linphone.org/projects/linphone/linphone-android/zh_Hant/
2023-05-09 09:07:16 +02:00
Ville Hartikainen
01a10fb806 Translated using Weblate (Finnish)
Currently translated at 54.5% (430 of 788 strings)

Translation: Linphone/Linphone Android (master)
Translate-URL: https://weblate.linphone.org/projects/linphone/linphone-android/fi/
2023-05-09 09:07:10 +02:00
Peter Chen
48b185ae0c Translated using Weblate (Chinese (Traditional))
Currently translated at 100.0% (788 of 788 strings)

Translation: Linphone/Linphone Android (master)
Translate-URL: https://weblate.linphone.org/projects/linphone/linphone-android/zh_Hant/
2023-05-09 09:07:05 +02:00
Peter Chen
7a541ff797 Translated using Weblate (Chinese (Traditional))
Currently translated at 100.0% (785 of 785 strings)

Translation: Linphone/Linphone Android (master)
Translate-URL: https://weblate.linphone.org/projects/linphone/linphone-android/zh_Hant/
2023-05-09 09:06:58 +02:00
George Salukvadze
f895d2ac19 Translated using Weblate (Georgian)
Currently translated at 100.0% (783 of 783 strings)

Translation: Linphone/Linphone Android (master)
Translate-URL: https://weblate.linphone.org/projects/linphone/linphone-android/ka/
2023-05-09 09:06:52 +02:00
NPL
7e4cf381be Translated using Weblate (Japanese)
Currently translated at 98.8% (774 of 783 strings)

Translation: Linphone/Linphone Android (master)
Translate-URL: https://weblate.linphone.org/projects/linphone/linphone-android/ja/
2023-05-09 09:06:47 +02:00
Peter Chen
4392da062b Translated using Weblate (Chinese (Traditional))
Currently translated at 100.0% (783 of 783 strings)

Translation: Linphone/Linphone Android (master)
Translate-URL: https://weblate.linphone.org/projects/linphone/linphone-android/zh_Hant/
2023-05-09 09:06:41 +02:00
Tomas Ivanek
1c46d9ae3c Translated using Weblate (Czech)
Currently translated at 100.0% (783 of 783 strings)

Translation: Linphone/Linphone Android (master)
Translate-URL: https://weblate.linphone.org/projects/linphone/linphone-android/cs/
2023-05-09 09:06:35 +02:00
NPL
d5e4e71a68 Translated using Weblate (Japanese)
Currently translated at 98.8% (774 of 783 strings)

Translation: Linphone/Linphone Android (master)
Translate-URL: https://weblate.linphone.org/projects/linphone/linphone-android/ja/
2023-05-09 09:06:30 +02:00
Peter Chen
348ab4604c Translated using Weblate (Chinese (Traditional))
Currently translated at 99.7% (797 of 799 strings)

Translation: Linphone/Linphone Android (master)
Translate-URL: https://weblate.linphone.org/projects/linphone/linphone-android/zh_Hant/
2023-05-09 08:59:26 +02:00
Peter Chen
d80094f2e6 Translated using Weblate (Chinese (Traditional))
Currently translated at 99.8% (795 of 796 strings)

Translation: Linphone/Linphone Android (master)
Translate-URL: https://weblate.linphone.org/projects/linphone/linphone-android/zh_Hant/
2023-05-06 05:30:07 +02:00
Tomas Ivanek
834f141320 Translated using Weblate (Czech)
Currently translated at 100.0% (794 of 794 strings)

Translation: Linphone/Linphone Android (master)
Translate-URL: https://weblate.linphone.org/projects/linphone/linphone-android/cs/
2023-05-06 05:30:07 +02:00
Tomas Ivanek
b4c4bc117d Translated using Weblate (Czech)
Currently translated at 100.0% (794 of 794 strings)

Translation: Linphone/Linphone Android (master)
Translate-URL: https://weblate.linphone.org/projects/linphone/linphone-android/cs/
2023-05-06 05:30:07 +02:00
Peter Chen
95afa84cbd Translated using Weblate (Chinese (Traditional))
Currently translated at 100.0% (794 of 794 strings)

Translation: Linphone/Linphone Android (master)
Translate-URL: https://weblate.linphone.org/projects/linphone/linphone-android/zh_Hant/
2023-05-06 05:30:07 +02:00
NPL
ff5d973cdb Translated using Weblate (Japanese)
Currently translated at 99.2% (787 of 793 strings)

Translation: Linphone/Linphone Android (master)
Translate-URL: https://weblate.linphone.org/projects/linphone/linphone-android/ja/
2023-05-06 05:30:07 +02:00
Peter Chen
c9270c5b81 Translated using Weblate (Chinese (Traditional))
Currently translated at 100.0% (793 of 793 strings)

Translation: Linphone/Linphone Android (master)
Translate-URL: https://weblate.linphone.org/projects/linphone/linphone-android/zh_Hant/
2023-05-06 05:30:07 +02:00
Tomas Ivanek
d220a8e6e0 Translated using Weblate (Czech)
Currently translated at 100.0% (793 of 793 strings)

Translation: Linphone/Linphone Android (master)
Translate-URL: https://weblate.linphone.org/projects/linphone/linphone-android/cs/
2023-05-06 05:30:07 +02:00
Peter Chen
eb81c498b9 Translated using Weblate (Chinese (Traditional))
Currently translated at 100.0% (793 of 793 strings)

Translation: Linphone/Linphone Android (master)
Translate-URL: https://weblate.linphone.org/projects/linphone/linphone-android/zh_Hant/
2023-05-06 05:30:07 +02:00
Ville Hartikainen
ae19526fe0 Translated using Weblate (Finnish)
Currently translated at 54.5% (430 of 788 strings)

Translation: Linphone/Linphone Android (master)
Translate-URL: https://weblate.linphone.org/projects/linphone/linphone-android/fi/
2023-05-06 05:30:07 +02:00
Peter Chen
613b7bdb17 Translated using Weblate (Chinese (Traditional))
Currently translated at 100.0% (788 of 788 strings)

Translation: Linphone/Linphone Android (master)
Translate-URL: https://weblate.linphone.org/projects/linphone/linphone-android/zh_Hant/
2023-05-06 05:30:07 +02:00
Peter Chen
d3a333d9bd Translated using Weblate (Chinese (Traditional))
Currently translated at 100.0% (785 of 785 strings)

Translation: Linphone/Linphone Android (master)
Translate-URL: https://weblate.linphone.org/projects/linphone/linphone-android/zh_Hant/
2023-05-06 05:30:07 +02:00
George Salukvadze
f6cf858089 Translated using Weblate (Georgian)
Currently translated at 100.0% (783 of 783 strings)

Translation: Linphone/Linphone Android (master)
Translate-URL: https://weblate.linphone.org/projects/linphone/linphone-android/ka/
2023-05-06 05:30:07 +02:00
NPL
63462c35f2 Translated using Weblate (Japanese)
Currently translated at 98.8% (774 of 783 strings)

Translation: Linphone/Linphone Android (master)
Translate-URL: https://weblate.linphone.org/projects/linphone/linphone-android/ja/
2023-05-06 05:30:07 +02:00
Peter Chen
73e4ec358a Translated using Weblate (Chinese (Traditional))
Currently translated at 100.0% (783 of 783 strings)

Translation: Linphone/Linphone Android (master)
Translate-URL: https://weblate.linphone.org/projects/linphone/linphone-android/zh_Hant/
2023-05-06 05:30:07 +02:00
Tomas Ivanek
5eac7212a5 Translated using Weblate (Czech)
Currently translated at 100.0% (783 of 783 strings)

Translation: Linphone/Linphone Android (master)
Translate-URL: https://weblate.linphone.org/projects/linphone/linphone-android/cs/
2023-05-06 05:30:07 +02:00
NPL
6d08487154 Translated using Weblate (Japanese)
Currently translated at 98.8% (774 of 783 strings)

Translation: Linphone/Linphone Android (master)
Translate-URL: https://weblate.linphone.org/projects/linphone/linphone-android/ja/
2023-05-06 05:30:07 +02:00
Sylvain Berfini
4d90ee0f15 Fixed build with 5.3-alpha SDK due to enum relocations changes 2023-05-05 15:40:59 +02:00
Sylvain Berfini
951a4aeb50 Fixed CHANGELOG format 2023-05-05 15:38:58 +02:00
Sylvain Berfini
981ee0ac8e Revert changing expires from 1 year to 1 month 2023-05-05 15:18:38 +02:00
Sylvain Berfini
6bb6daadba Switched account creator to FlexiAPI backend instead of XMLRPC 2023-05-05 15:00:30 +02:00
Sylvain Berfini
f551398652 Bumped dependencies 2023-05-05 10:44:52 +02:00
Sylvain Berfini
ea32b92465 Open keyboard when replying to a message if no text / file / voice record is pending + bumped gradle 2023-05-04 10:31:58 +02:00
Sylvain Berfini
77993d74aa Fixed E2E icon visible in contacts list even when LIME isn't available + add warning when authentication required callback is triggered 2023-05-03 10:28:22 +02:00
Sylvain Berfini
48a68ab5c2 Added account setting allowing to disable bundle mode 2023-05-02 17:11:06 +02:00
Sylvain Berfini
e8f94a489f Add/remove friend list listener when created/destroyed while app is alive 2023-04-27 07:08:38 +00:00
Sylvain Berfini
a2535cef56 Hidden vibrator setting if the device has no vibrator 2023-04-27 08:59:14 +02:00
Sylvain Berfini
48fb7dfa92 Improved patterns & URI click handling in chat messages 2023-04-25 14:03:06 +02:00
Sylvain Berfini
0733ced356 Fixed contacts not updated in conversation if found after view was layed out 2023-04-24 12:05:40 +02:00
Christophe Deschamps
e725eb4e7b - Upon GSM interruption handle conference cases by leave()/enter()
- Upon StreamsRunning call state update in conference check and set locallyPaused flag.
2023-04-24 10:42:20 +02:00
Sylvain Berfini
d54a707816 Decline incoming call with busy reason if more than one call 2023-04-20 10:48:49 +02:00
Sylvain Berfini
91601cdd8c Bumped dependencies, improved a bit build time 2023-04-20 10:05:50 +02:00
Sylvain Berfini
22c7783c18 Fixed emoji picker icon color in dark mode 2023-04-19 11:33:29 +02:00
Sylvain Berfini
7607a5cd57 Using new API to properly know the last seen online timestamp and simply display Away when info isn't available 2023-04-17 16:24:49 +02:00
Sylvain Berfini
c313e06f8f Fixed wrong format in some translated string + added try/catch to prevent a future one from making the app crash 2023-04-17 14:13:15 +02:00
Sylvain Berfini
33b4c09ffd Bumped dependencies & improved gradle files + new bullseye based clean docker image using JDK 17 2023-04-14 15:39:45 +02:00
Sylvain Berfini
b80f2fc9a2 Make nav bar match tabs background color 2023-04-14 09:39:25 +02:00
Sylvain Berfini
2e9ed6af4a Forgot to remove test code 2023-04-13 09:19:26 +02:00
Sylvain Berfini
e60cc20a23 Updated strings regarding software echo canceller + reset calibrated value when disabling it + display currently calibrated value if any 2023-04-12 13:53:40 +02:00
Sylvain Berfini
122581fd68 Fixed chat rooms list cells not updating contacts info 2023-04-12 10:29:25 +02:00
Sylvain Berfini
f3a6480278 Use EmojiCompat.loadState to prevent crash 2023-04-11 15:57:07 +02:00
Sylvain Berfini
ae54179976 Fixed chat message not marked as reply when replying with more than 1 content in a basic chat room 2023-04-11 12:06:24 +02:00
Sylvain Berfini
a8d8690d92 Made SIP URI in call selectable 2023-04-11 08:42:56 +02:00
Sylvain Berfini
98f6206e75 Another fix related to callLogs crash reported on the Play Store 2023-04-11 08:34:47 +02:00
Sylvain Berfini
222d60cfdd Fixed chat message sending area icons size when input text takes more than one line 2023-04-07 15:35:56 +02:00
Sylvain Berfini
0f67ca79dd Should improve chat rooms list display performances a bit 2023-04-07 11:26:52 +02:00
Sylvain Berfini
10e29db302 Added / improved some logs 2023-04-07 11:04:35 +02:00
Sylvain Berfini
c2a649c50e Prevent crash in emoji compat library when used right after the app started (cold start for a chat room shorcut for example) 2023-04-07 09:45:54 +02:00
Sylvain Berfini
8cd42efa8e Improved chat room landscape UI when not much height is available (such as phones) 2023-04-06 17:23:32 +02:00
Sylvain Berfini
a11e66f404 Improved how we detect soft keyboard visibility + using that to hide emoji picker when keyboard is opened 2023-04-06 15:49:47 +02:00
Sylvain Berfini
e395cff106 Close keyboard when opening emoji picker + improved EmojiCompat init 2023-04-06 14:31:40 +02:00
Sylvain Berfini
22d4a8baf1 Added emoji picker in chat 2023-04-06 13:40:40 +02:00
Sylvain Berfini
b1b426389d Fixed UI issue when hanging up an active call when an incoming call is ringing 2023-04-04 14:47:09 +02:00
Sylvain Berfini
182d80a630 Added option to protect settings by account password 2023-04-03 13:59:08 +02:00
Sylvain Berfini
29ee69fc7b Fixed contact's initials display as avatar when containing more than one emoji (or an emoji + another character) 2023-04-03 11:36:35 +02:00
Sylvain Berfini
d313b31e12 Updated CHANGELOG with 5.0.9 info 2023-04-03 09:52:57 +02:00
Sylvain Berfini
09d5868820 Rework how the app handles the removal of plain copy of encrypted files when VFS is enabled 2023-04-03 09:52:07 +02:00
Sylvain Berfini
31e30e6214 Proper fix for chat rooms blinking 2023-03-31 16:43:00 +02:00
Sylvain Berfini
8338ea8814 Renamed setting 2023-03-30 16:14:22 +02:00
Sylvain Berfini
a629c3264d Prevent crash when clicking on URI in chat if not matching app is found 2023-03-30 15:07:31 +02:00
Sylvain Berfini
1767ae559b Added settings to hide contacts list selectors 2023-03-28 15:38:52 +02:00
Sylvain Berfini
df4d136305 Added linphone-config scheme to (automatically or not) do a remote provisioning step using an URL 2023-03-28 15:26:15 +02:00
Sylvain Berfini
ea190fdfb7 Fixed admins not shown for non admins in group chat room info 2023-03-28 12:01:28 +02:00
Sylvain Berfini
455165f00b Prevent blinking in chat rooms list 2023-03-27 13:01:48 +02:00
Sylvain Berfini
b2a7ae7629 Added Weblate translations graph widget to README 2023-03-27 11:19:40 +02:00
Sylvain Berfini
2fd0b87467 Added IMDN status in chat rooms list for last message if outgoing + removed sender name in front of last message for 1-1 chat rooms 2023-03-27 11:19:15 +02:00
Sylvain Berfini
7136a991b3 Fixed presence led background in dark mode + reduced background padding a bit 2023-03-27 11:04:09 +02:00
Sylvain Berfini
b467569308 Improved code handling how presence icon & content description is computed + added do not disturb presence support 2023-03-24 10:15:11 +01:00
Sylvain Berfini
4264ecc933 Bumped dependencies, now side menu will be closed (if open) with back button press or back gesture 2023-03-23 17:08:52 +01:00
Sylvain Berfini
bca39042d7 Enable timestamp update in presence refresh 2023-03-23 16:44:22 +01:00
Andrea Gianarda
1af9954606 Setup texture view listener for participant devices in platform helpers 2023-03-22 14:11:19 +01:00
Sylvain Berfini
36047921b8 Fixed LIME update threshold 2023-03-22 11:49:53 +01:00
Sylvain Berfini
360f30df4b Fixed visible presence in side menu when no account configured yet 2023-03-20 16:15:19 +01:00
Sylvain Berfini
048b1c91a3 Bumped year to 2023 for license in about page 2023-03-20 16:04:17 +01:00
Sylvain Berfini
b0e25f9a67 Added a setting to use native addressbook in read only 2023-03-20 14:40:38 +01:00
Sylvain Berfini
a8bdca0c26 Added RC setting to disable video completely 2023-03-20 13:48:12 +01:00
Sylvain Berfini
feec8b9758 Trying to prevent crash in call history trying to get CallLog ID 2023-03-20 10:34:26 +01:00
Sylvain Berfini
09bde054d0 Added attented transfer + setting to skip dialer when adding new call or transfering a call + fixed toast instead of snack for dialer transfer message 2023-03-16 11:15:17 +01:00
Sylvain Berfini
74fd59a541 Fixed translations 2023-03-16 10:06:30 +01:00
Tomas Ivanek
0e4c07ef8a Translated using Weblate (Czech)
Currently translated at 100.0% (779 of 779 strings)

Translation: Linphone/Linphone Android (master)
Translate-URL: https://weblate.linphone.org/projects/linphone/linphone-android/cs/
2023-03-14 04:30:17 +01:00
Ville Hartikainen
0169ab6af8 Translated using Weblate (Finnish)
Currently translated at 49.6% (387 of 779 strings)

Translation: Linphone/Linphone Android (master)
Translate-URL: https://weblate.linphone.org/projects/linphone/linphone-android/fi/
2023-03-14 04:30:17 +01:00
NPL
7bba871907 Translated using Weblate (Japanese)
Currently translated at 97.5% (760 of 779 strings)

Translation: Linphone/Linphone Android (master)
Translate-URL: https://weblate.linphone.org/projects/linphone/linphone-android/ja/
2023-03-14 04:30:17 +01:00
Peter Chen
6ba12d3a7d Translated using Weblate (Chinese (Traditional))
Currently translated at 100.0% (779 of 779 strings)

Translation: Linphone/Linphone Android (master)
Translate-URL: https://weblate.linphone.org/projects/linphone/linphone-android/zh_Hant/
2023-03-14 04:30:17 +01:00
Adam Piorko
bdb72d4c2f Added translation using Weblate (Polish) 2023-03-14 04:30:17 +01:00
Sylvain Berfini
8c6975a859 Added account removal confirmation dialog 2023-03-13 13:06:17 +01:00
Sylvain Berfini
c39f350bdd Bumped dependencies 2023-03-13 10:42:02 +01:00
Sylvain Berfini
0df5ff6c30 Improved how crashlytics dependency is added 2023-03-02 17:10:30 +01:00
Sylvain Berfini
6859a321cc Fixed icon/text color of file/conference invite in reply preview/cell not visible in dark mode 2023-03-01 10:01:49 +01:00
Sylvain Berfini
76455c1491 Fixed crash when creating CoreContext from BroadcastReceiver context 2023-03-01 09:42:25 +01:00
Sylvain Berfini
3aedf9be45 Bumped dependencies 2023-02-28 13:34:48 +01:00
Sylvain Berfini
4c6155b305 Updated presence's publish's expires to 2 minutes 2023-02-28 10:08:36 +01:00
Sylvain Berfini
5a14a2d1eb Updated CHANGELOG with 5.0.7 release notes 2023-02-27 13:58:47 +01:00
Sylvain Berfini
9054420536 Bumped security-crypto-ktx dependency 2023-02-27 11:11:31 +01:00
Sylvain Berfini
9a086ef205 Fixed assistant createButton not enabled & country name not displayed if prefix auto filled 2023-02-27 11:11:07 +01:00
Ville Hartikainen
9726ed04ea Translated using Weblate (Finnish)
Currently translated at 31.0% (242 of 779 strings)

Translation: Linphone/Linphone Android (master)
Translate-URL: https://weblate.linphone.org/projects/linphone/linphone-android/fi/
2023-02-27 09:58:19 +01:00
NPL
812ed6ec68 Translated using Weblate (Japanese)
Currently translated at 97.0% (749 of 772 strings)

Translation: Linphone/Linphone Android (master)
Translate-URL: https://weblate.linphone.org/projects/linphone/linphone-android/ja/
2023-02-25 04:30:06 +01:00
Sylvain Berfini
dc65d27603 Show contacts consolidated presence if not offline + updated EXPIRE for sip.linphone.org from 1 year to 1 month 2023-02-24 11:42:45 +01:00
Sylvain Berfini
536bfd0020 Fixed navigating to contact without a native ID 2023-02-24 11:26:53 +01:00
Sylvain Berfini
9f5951280a Added support for base64 encoded pictures in Friends 2023-02-24 10:17:28 +01:00
Sylvain Berfini
785be0b1fd Do not update friend list subscriptions when contacts loading has finished if default account isn't registered yet 2023-02-21 12:04:55 +01:00
Sylvain Berfini
8bcc9a2087 Fixed cs & fi translations, added them to locales_config 2023-02-20 15:10:11 +01:00
Sylvain Berfini
4c6ead64f4 Added config option to show secure chat button in contact details & call history even if no LIME capability in presence (or no presence at all) 2023-02-20 15:07:54 +01:00
Sylvain Berfini
5c657261dd Translated using Weblate (Czech)
Currently translated at 100.0% (772 of 772 strings)

Translation: Linphone/Linphone Android (master)
Translate-URL: https://weblate.linphone.org/projects/linphone/linphone-android/cs/
2023-02-20 09:58:09 +01:00
Tomas Ivanek
2033e8d6af Translated using Weblate (Czech)
Currently translated at 100.0% (772 of 772 strings)

Translation: Linphone/Linphone Android (master)
Translate-URL: https://weblate.linphone.org/projects/linphone/linphone-android/cs/
2023-02-20 09:58:09 +01:00
NPL
864b42b98c Translated using Weblate (Japanese)
Currently translated at 93.7% (724 of 772 strings)

Translation: Linphone/Linphone Android (master)
Translate-URL: https://weblate.linphone.org/projects/linphone/linphone-android/ja/
2023-02-20 09:58:09 +01:00
Tomas Ivanek
06b65d616e Translated using Weblate (Czech)
Currently translated at 100.0% (772 of 772 strings)

Translation: Linphone/Linphone Android (master)
Translate-URL: https://weblate.linphone.org/projects/linphone/linphone-android/cs/
2023-02-18 21:58:39 +01:00
Ville Hartikainen
00761f9106 Translated using Weblate (Finnish)
Currently translated at 31.0% (240 of 772 strings)

Translation: Linphone/Linphone Android (master)
Translate-URL: https://weblate.linphone.org/projects/linphone/linphone-android/fi/
2023-02-18 21:58:26 +01:00
NPL
bf72baf112 Translated using Weblate (Japanese)
Currently translated at 88.0% (680 of 772 strings)

Translation: Linphone/Linphone Android (master)
Translate-URL: https://weblate.linphone.org/projects/linphone/linphone-android/ja/
2023-02-18 21:58:23 +01:00
Nono Hoolbe
3f1715a401 Added translation using Weblate (Czech) 2023-02-18 13:33:24 +01:00
稲凪 咲
e348576f3d Translated using Weblate (Japanese)
Currently translated at 76.6% (592 of 772 strings)

Translation: Linphone/Linphone Android (master)
Translate-URL: https://weblate.linphone.org/projects/linphone/linphone-android/ja/
2023-02-18 04:36:55 +01:00
NPL
2f40a5c74d Translated using Weblate (Japanese)
Currently translated at 76.6% (592 of 772 strings)

Translation: Linphone/Linphone Android (master)
Translate-URL: https://weblate.linphone.org/projects/linphone/linphone-android/ja/
2023-02-18 04:36:55 +01:00
Sylvain Berfini
083c1afa11 Prevent crash when destroying GroupedCallLogData if lastCallLogViewModel wasn't created yet 2023-02-15 13:26:54 +01:00
Sylvain Berfini
890d217ab7 Disable some actions when a chat room has been left 2023-02-15 10:03:28 +01:00
Sylvain Berfini
375f43cfa5 Fixed chat messages grouping 2023-02-13 16:13:56 +01:00
Sylvain Berfini
61ecc91a2d Fixed issue with voice recording preview progress bar 2023-02-13 13:25:07 +01:00
Sylvain Berfini
28883abe80 Moved chat message sending layout to specific file & fixed separator being visible upon rotation 2023-02-13 10:54:40 +01:00
Sylvain Berfini
5e5937382d Fixed issue when clicking on send message while voice recording is in progress 2023-02-10 14:32:30 +01:00
Sylvain Berfini
312f8c51c2 Fixed appName entity issues 2023-02-10 11:33:38 +01:00
Ville Hartikainen
e6fb014d6b Translated using Weblate (Finnish)
Currently translated at 12.5% (97 of 772 strings)

Translation: Linphone/Linphone Android (master)
Translate-URL: https://weblate.linphone.org/projects/linphone/linphone-android/fi/
2023-02-10 11:28:55 +01:00
samuele
0317f59f0b Translated using Weblate (Italian)
Currently translated at 25.1% (194 of 772 strings)

Translation: Linphone/Linphone Android (master)
Translate-URL: https://weblate.linphone.org/projects/linphone/linphone-android/it/
2023-02-10 11:28:55 +01:00
Ville Hartikainen
5380ef0348 Translated using Weblate (Finnish)
Currently translated at 10.3% (80 of 772 strings)

Translation: Linphone/Linphone Android (master)
Translate-URL: https://weblate.linphone.org/projects/linphone/linphone-android/fi/
2023-02-10 11:28:55 +01:00
NPL
12731d2a35 Translated using Weblate (Japanese)
Currently translated at 73.5% (568 of 772 strings)

Translation: Linphone/Linphone Android (master)
Translate-URL: https://weblate.linphone.org/projects/linphone/linphone-android/ja/
2023-02-10 11:28:55 +01:00
Ville Hartikainen
af3ef13063 Added translation using Weblate (Finnish) 2023-02-10 11:28:55 +01:00
NPL
1865a2a749 Translated using Weblate (Japanese)
Currently translated at 71.2% (550 of 772 strings)

Translation: Linphone/Linphone Android (master)
Translate-URL: https://weblate.linphone.org/projects/linphone/linphone-android/ja/
2023-02-10 11:28:55 +01:00
NPL
5c992b1aa8 Translated using Weblate (Japanese)
Currently translated at 71.2% (550 of 772 strings)

Translation: Linphone/Linphone Android (master)
Translate-URL: https://weblate.linphone.org/projects/linphone/linphone-android/ja/
2023-02-10 11:28:55 +01:00
NPL
ad280076c0 Translated using Weblate (Japanese)
Currently translated at 61.2% (473 of 772 strings)

Translation: Linphone/Linphone Android (master)
Translate-URL: https://weblate.linphone.org/projects/linphone/linphone-android/ja/
2023-02-10 11:28:55 +01:00
NPL
6279daa8de Translated using Weblate (Japanese)
Currently translated at 39.6% (306 of 772 strings)

Translation: Linphone/Linphone Android (master)
Translate-URL: https://weblate.linphone.org/projects/linphone/linphone-android/ja/
2023-02-10 11:28:55 +01:00
NPL
fbe6526eb1 Translated using Weblate (Japanese)
Currently translated at 24.4% (189 of 772 strings)

Translation: Linphone/Linphone Android (master)
Translate-URL: https://weblate.linphone.org/projects/linphone/linphone-android/ja/
2023-02-10 11:28:55 +01:00
Peter Chen
d132b9b0a3 Translated using Weblate (Chinese (Traditional))
Currently translated at 100.0% (772 of 772 strings)

Translation: Linphone/Linphone Android (master)
Translate-URL: https://weblate.linphone.org/projects/linphone/linphone-android/zh_Hant/
2023-02-10 11:28:55 +01:00
NPL
66edb57cd0 Translated using Weblate (Japanese)
Currently translated at 12.0% (93 of 770 strings)

Translation: Linphone/Linphone Android (master)
Translate-URL: https://weblate.linphone.org/projects/linphone/linphone-android/ja/
2023-02-10 11:28:55 +01:00
Dries De Gendt
522c77493e Translated using Weblate (Dutch)
Currently translated at 20.3% (157 of 770 strings)

Translation: Linphone/Linphone Android (master)
Translate-URL: https://weblate.linphone.org/projects/linphone/linphone-android/nl/
2023-02-10 11:28:55 +01:00
NPL
73e5f06333 Added translation using Weblate (Japanese) 2023-02-10 11:28:55 +01:00
Till von Elling
4fda4cfc6b Translated using Weblate (German)
Currently translated at 34.4% (265 of 770 strings)

Translation: Linphone/Linphone Android (master)
Translate-URL: https://weblate.linphone.org/projects/linphone/linphone-android/de/
2023-02-10 11:28:55 +01:00
Peter Chen
889c42a4c6 Translated using Weblate (Chinese (Traditional))
Currently translated at 100.0% (770 of 770 strings)

Translation: Linphone/Linphone Android (master)
Translate-URL: https://weblate.linphone.org/projects/linphone/linphone-android/zh_Hant/
2023-02-10 11:28:55 +01:00
Sylvain Berfini
35102a8af8 Bumped dependencies 2023-02-10 10:38:21 +01:00
Sylvain Berfini
af8464d89e Updated kotlin 2023-02-07 17:34:51 +01:00
Sylvain Berfini
fa6cb4515a Improved proximity sensor related logs 2023-02-07 10:22:13 +01:00
Sylvain Berfini
d38aaeec9c Updated gradle 2023-02-07 10:21:59 +01:00
Sylvain Berfini
a68a816cff Prevent multiple attach file dialogs 2023-02-03 09:39:18 +01:00
Sylvain Berfini
4d1adf6227 Fixed clickable SIP URI pattern, added missing tilde
Updated CHANGELOG
2023-02-02 14:03:25 +01:00
Sylvain Berfini
7c3722a90a Fixed pixel issue with call_quality_indicator_2 PNG 2023-02-02 14:02:43 +01:00
Sylvain Berfini
9cf50d3c06 Trying to prevent crash when CallActivity is destroyed after the Core 2023-02-02 14:02:23 +01:00
Sylvain Berfini
0e3c82b519 Hide echo tester setting if debug mode isn't enabled as it can mislead users over what it can be used for 2023-02-02 14:02:18 +01:00
Sylvain Berfini
b463025a0e Fixed wrong country being displayed when picking a prefix if there are multiple countries matching that prefix 2023-02-02 14:02:11 +01:00
Christophe Deschamps
4ff06f8494 Headers text color inside IMDN Fragment 2023-02-01 16:14:45 +01:00
Christophe Deschamps
b418aa2732 Visibility of ephemeral icon next to send icon in chat room details 2023-01-31 15:50:23 +01:00
Sylvain Berfini
832a75726f Updated CHANGELOG 2023-01-27 14:25:44 +01:00
Sylvain Berfini
382805ddeb Improved incoming call fragment layout when early media with video 2023-01-27 14:22:24 +01:00
Sylvain Berfini
31f3cb3391 Fixed chat message replies in notification 2023-01-27 14:22:17 +01:00
Sylvain Berfini
327f296ac0 Fixed local copy of attached files being leaked if it isn't being sent 2023-01-27 14:22:10 +01:00
Sylvain Berfini
b3d0897897 Reworked chat message sending area to add a spinner while files are being loaded 2023-01-27 14:22:05 +01:00
Sylvain Berfini
98d7a6b5d4 Disabled crashlytics for debug builds 2023-01-27 14:22:00 +01:00
Sylvain Berfini
edefe2dc07 Disabled auto-complete in some EditText to prevent username to be modified, breaking SIP URI format 2023-01-27 14:21:55 +01:00
Sylvain Berfini
97cba944c0 Removed jetifier + bumped version name & code 2023-01-13 15:17:08 +01:00
Sylvain Berfini
31826d5b43 Display voice recording default message instead of file name, like for conference invitations 2023-01-13 15:13:45 +01:00
Sylvain Berfini
cc2b97fba4 Updated CHANGELOG with 5.0.3 changes 2023-01-13 14:43:41 +01:00
Sylvain Berfini
0e6a2017f3 Updated gradle 2023-01-13 14:42:27 +01:00
Sylvain Berfini
c307dbc1de Leverage on Bitmap stored in ContactsManager to prevent decoding it from resource each time 2023-01-13 14:42:20 +01:00
Sylvain Berfini
01c8735caf Reworked how Persons are used + fixed timestamp issue in chat message notification + fixed chat room being marked as read when answering in the notification 2023-01-13 14:42:14 +01:00
Sylvain Berfini
8a09115623 Replaced old emoji library by emoji2 2023-01-13 14:42:08 +01:00
Sylvain Berfini
84fbeae5a6 Bumped appcompat dependency 2023-01-13 14:42:03 +01:00
Sylvain Berfini
221c893f45 Changes required for chat message notification to show up in Android Auto 2023-01-13 14:41:58 +01:00
Sylvain Berfini
40c68f68f0 Also updated algo to select hearing aid/bluetooth playback device for call recordings 2023-01-13 14:41:52 +01:00
Sylvain Berfini
5dcd9faba5 Updated Coil to 2.2.2 release 2023-01-13 14:41:46 +01:00
Sylvain Berfini
0ecf331959 Reworked a bit the code handling the file type depending on it's extension + added error logs when Coil fails to load an image 2023-01-10 16:24:22 +01:00
Sylvain Berfini
67081ee577 Delay voice recorder init 2023-01-10 16:24:17 +01:00
Sylvain Berfini
68f0d4102c Updated Firebase BoM 2023-01-10 16:24:12 +01:00
Sylvain Berfini
e784ca7b77 Reworked Media Store export functions in compatibility packages 2023-01-10 16:24:07 +01:00
Sylvain Berfini
fdaaf0943c Reduced previously increased icons size 2023-01-09 17:48:15 +01:00
Sylvain Berfini
85580121a7 Made chat message sending icons size bigger 2023-01-09 17:09:01 +01:00
Sylvain Berfini
d839aef96e Hide actively speaking participant miniature 2023-01-09 17:08:56 +01:00
Sylvain Berfini
ccd7bcea62 Fixed ANRs in voice message recording/playback + Allow voice recording Bluetooth playback + allow voice message recording using headset/headphones/hearing aids/bluetooth device 2023-01-09 17:08:50 +01:00
Sylvain Berfini
de32e634a0 Updated CHANGELOG with 5.0.1 and 5.0.2 changes 2023-01-05 13:25:08 +01:00
Sylvain Berfini
9e7062ed8d Fixed some files not exported to MediaStore 2023-01-05 13:24:03 +01:00
Sylvain Berfini
11776db13c Allow transfered files through chat to be automatically exported to native gallery 2023-01-05 13:23:57 +01:00
Sylvain Berfini
32d111e613 Prevent trying to resolve native contact if permission hasn't been granted 2023-01-05 13:23:51 +01:00
Sylvain Berfini
dbe0655f0f Prevent crash due to CountryPickerFragment not having a constructor without params 2023-01-05 13:23:45 +01:00
Sylvain Berfini
584880e4df Prevent broken deep link URI if SIP URI to add to contact is null or empty 2023-01-05 13:23:40 +01:00
Sylvain Berfini
6db5b54a86 Ensure sip.linphone.org accounts have a LIME X3DH server URL set in their params 2023-01-05 13:23:35 +01:00
Sylvain Berfini
3fc596c448 Fixed crash if null audioState returned by TelecomManager's Connection 2023-01-05 13:23:27 +01:00
Sylvain Berfini
6cebb8cb92 Fixed switch from audio only to active speaker or mosaic that enables the video without user consent + improved some conference logs 2022-12-15 10:22:18 +01:00
Sylvain Berfini
2ca76ef44f Hide participant count in cancelled conference ICS 2022-12-14 10:47:28 +01:00
Sylvain Berfini
662f01eee4 Fixed wrong participant LIME status being displayed 2022-12-12 09:59:17 +01:00
Sylvain Berfini
af1d12fa2f Fixed IMDN status when file transfer goes into error state 2022-12-12 09:59:06 +01:00
Sylvain Berfini
5a6531a75f Improved SIP URI pattern regex to prevent http URLS containing '@' to be handled as SIP URI 2022-12-12 09:58:58 +01:00
Sylvain Berfini
8f0f71d82b Bumped version code & updated CHANGELOG for 5.0.0 release 2022-12-06 11:35:32 +01:00
Sylvain Berfini
6d2ae8007a Reworked a bit & improved video call take snapshot process 2022-12-06 11:14:20 +01:00
Sylvain Berfini
163e2adfde Added setting to allow to switch to fullscreen automatically when a video call is answered (disabled by default) 2022-12-06 10:48:29 +01:00
Sylvain Berfini
59eb436423 Fixed build due to @Nullable changes in SDK API 2022-12-05 15:25:05 +01:00
andy wu
a1d1bc035e Translated using Weblate (Chinese (Simplified))
Currently translated at 100.0% (769 of 769 strings)

Translation: Linphone/Linphone Android (4.7 release)
Translate-URL: https://weblate.linphone.org/projects/linphone/linphone-android-4-7-release/zh_Hans/
2022-12-05 15:25:01 +01:00
Sylvain Berfini
8f47248195 Bumped version code 2022-12-05 15:15:17 +01:00
Sylvain Berfini
54a65f713a Fixed NAT policy account settings changes not saved/restored when app restarts 2022-12-05 10:43:57 +01:00
Sylvain Berfini
b511867e62 Fixed participants count in ICS if organizer is part of participants list 2022-12-05 09:38:41 +01:00
Sylvain Berfini
b067f83784 Prevent crashes found on crashlytics 2022-11-25 13:30:23 +01:00
Sylvain Berfini
959f87b425 Fixed phone number avatar in incoming/outgoing and simple call views 2022-11-24 16:51:09 +01:00
Sylvain Berfini
9fbd4fbc40 Use FEATURE_TELECOM instead of FEATURE_CONNECTION_SERVICE for Android >= 33 + added logs to TelecomHelper 2022-11-24 16:51:04 +01:00
Sylvain Berfini
226abd9e19 Fixed launcher shortcuts that weren't working anymore 2022-11-24 11:59:40 +01:00
Sylvain Berfini
92957bf1d2 Fixed avatar in conference if username starts with '+' 2022-11-24 11:18:35 +01:00
Sylvain Berfini
d8fb9a22b9 Fixed QR code assistant landscape UI 2022-11-24 10:17:19 +01:00
Sylvain Berfini
4d0c2398a0 Fixed participant icon still visible on cancelled meeting invite 2022-11-24 10:09:07 +01:00
Sylvain Berfini
edb55fdf6b Fixed display issue when going or leaving PiP mode in conference 2022-11-23 16:12:07 +01:00
Sylvain Berfini
80c0e6df36 Fixed some margin issues with local participant miniature in active speaker layout 2022-11-23 15:52:44 +01:00
Sylvain Berfini
08547ea663 Remove conference recording buttons for now 2022-11-23 15:01:38 +01:00
Sylvain Berfini
cf09f82523 Updated supported list of locales 2022-11-23 13:30:36 +01:00
Sylvain Berfini
66c2a74c8e Fixed ANR when playing call recording sometimes 2022-11-22 10:45:19 +01:00
Sylvain Berfini
774b37c73c Improved UI a bit 2022-11-22 10:35:13 +01:00
Sylvain Berfini
8e893b60a1 Fixed video toggle button in single call 2022-11-21 16:44:35 +01:00
Sylvain Berfini
f3178a0854 Prevent use of const friend in contact editor 2022-11-21 16:11:52 +01:00
Sylvain Berfini
fbed7f4f74 Fixed auto call recording 2022-11-21 14:47:34 +01:00
Sylvain Berfini
59bcb63dee Trim string before making a new call or creating a chat room 2022-11-21 14:19:00 +01:00
Sylvain Berfini
ee40995f34 Fixed issue with contact editor since we switched to Friend as original value from addressbook will be lost in Friend causing edition/removal in native addressbook to silently fail 2022-11-21 13:57:10 +01:00
Sylvain Berfini
11f36dcb63 Added setting to disable phone account linking + hide menu for non sip.linphone.org accounts 2022-11-18 11:41:03 +01:00
Sylvain Berfini
483443fd60 Prevent proximity sensor to turn screen off while on incoming/outgoing call fragments 2022-11-18 11:08:09 +01:00
Sylvain Berfini
0895db1a62 Fixed UI issue in call if at Android level the font size is increased + prevent 'null' call to be displayed sometimes 2022-11-18 10:59:51 +01:00
Sylvain Berfini
bb74f14736 Bumped version code 2022-11-17 10:57:12 +01:00
Sylvain Berfini
47a26144c1 Use compat APIs version of WindowInset 2022-11-16 15:42:23 +01:00
Sylvain Berfini
f0e8e6cd06 Fixed duplicated chat message that could happen when loading a chat room while some aggregrated messages weren't notified yet 2022-11-16 15:12:31 +01:00
Sylvain Berfini
791e27f479 Switch from audio only to active speaker using toggle video button (instead of disabling it) 2022-11-16 15:03:33 +01:00
Sylvain Berfini
cf3b68cc1b Fixed video conf not going full screen when joining 2022-11-15 17:18:31 +01:00
Sylvain Berfini
8b86f91c1f Fixed conference scheduler date picker showing wrong date for UTC-X timezones 2022-11-15 16:53:06 +01:00
Sylvain Berfini
66edc747be Bumped version code & firebase BoM 2022-11-14 10:08:51 +01:00
Sylvain Berfini
0520765bdc Fixed active speaking participant paused UI when only 2 in conf 2022-11-10 14:25:56 +01:00
Sylvain Berfini
1e637f0f7c Bumped version code 2022-11-09 09:47:59 +00:00
Peter Chen
6df00e7ee8 Translated using Weblate (Chinese (Traditional))
Currently translated at 100.0% (769 of 769 strings)

Translation: Linphone/Linphone Android (4.7 release)
Translate-URL: https://weblate.linphone.org/projects/linphone/linphone-android-4-7-release/zh_Hant/
2022-11-09 10:39:16 +01:00
Sylvain Berfini
007f823676 Fixed issues with actively speaking participant device for outgoing group calls 2022-11-08 11:11:21 +01:00
Sylvain Berfini
5bb5bd16ae Prevent crash if Security Exception is thrown in TelecomHelper.findExistingAccount (see on Oppo running Android 11 asking for READ_PHONE_STATE even if it shoudln't) 2022-11-07 16:42:40 +01:00
Sylvain Berfini
68fb96e3a1 Prevent pause logo to appear in front of avatar when joining conf 2022-11-03 15:45:04 +01:00
Sylvain Berfini
91a23cb286 Should fix the remaining ForegroundServiceStartNotAllowedException 2022-11-03 12:05:46 +01:00
Sylvain Berfini
f3ad232c1f Prevent crash when removing service task if Core isn't alive 2022-11-03 11:54:54 +01:00
Sylvain Berfini
b9c178cc29 Reworked ZRTP SAS validation dialog 2022-10-31 17:27:49 +01:00
Sylvain Berfini
d9dce51823 Revert latest changes regarding avatar to properly fix phone number avatar across app 2022-10-28 10:28:50 +02:00
Sylvain Berfini
ce3d333a98 Bumped version code 2022-10-28 09:44:51 +02:00
Sylvain Berfini
a6b3b33587 Hide scheduled meeting description if empty in summary + fix description textfield title in summary + fixed issue with avatars 2022-10-27 16:35:13 +02:00
Sylvain Berfini
24c653d039 Prevent audio route menu to be briefly visible when toggling speaker in conference waiting room if animations are enabled and no bluetooth device is connected 2022-10-27 10:24:57 +02:00
Sylvain Berfini
8721a68db5 Bumped dependencies 2022-10-26 17:12:37 +02:00
Sylvain Berfini
e01ffc0211 Fixed active speaker alone layout when in fact there is at least one other participant 2022-10-24 14:19:16 +02:00
Sylvain Berfini
59a9290832 Using new callback to display the correct active speaker participant device name 2022-10-20 11:46:08 +02:00
Sylvain Berfini
dd1ec48cbd Dismiss ZRTP SAS validation dialog when clicking outside 2022-10-18 15:47:07 +02:00
Sylvain Berfini
42846e6b45 Close layout & audio device popup menus when selecting an item in conference waiting room 2022-10-14 11:49:36 +02:00
Sylvain Berfini
f0fe5cc418 Fixed conference scheduling duration not being 1h 2022-10-13 15:57:41 +02:00
Sylvain Berfini
ea9d6c48bc Do not show ZRTP auth token validation dialog if the token is null 2022-10-13 10:03:20 +02:00
Sylvain Berfini
072db50727 Improved notification text when more than 1 file is inside the message + updated RC flags 2022-10-11 16:13:15 +02:00
Sylvain Berfini
eb63d63276 Fixed various UI glitches 2022-10-07 13:51:19 +02:00
Sylvain Berfini
f0cb889215 Catch foreground service not allowed to start exception 2022-10-07 08:53:22 +00:00
Sylvain Berfini
3c830760c2 Fixed UI issue when hanging up call if there is still another call to display 2022-10-05 17:25:28 +02:00
Sylvain Berfini
a230f603c6 Improvements on use of Telecom Manager APIs to prevent dialog asking to terminate call when trying to add a new call to an existing conference 2022-10-05 16:19:32 +02:00
Sylvain Berfini
db8b6f2dfb Fixed issue with active speaker layout 2022-10-03 10:30:51 +02:00
1803 changed files with 111035 additions and 71897 deletions

View file

@ -39,7 +39,7 @@ If you are using a SDK that isn't the latest release, please update first as it'
5. **SDK logs** (mandatory) 5. **SDK logs** (mandatory)
Enable debug logs in advanced section of the settings, restart the app, reproduce the issue and then go back to advanced settings, click on "Send logs" and copy/paste the link here. Click on "Share logs" in Help -> Troubleshooting view and copy/paste the link here.
It's also explained [in the README](https://github.com/BelledonneCommunications/linphone-android#behavior-issue). It's also explained [in the README](https://github.com/BelledonneCommunications/linphone-android#behavior-issue).

14
.github/ISSUE_TEMPLATE/config.yml vendored Normal file
View file

@ -0,0 +1,14 @@
blank_issues_enabled: false
contact_links:
- name: SDK issues
url: https://github.com/BelledonneCommunications/linphone-sdk/issues
about: Please post issues about the SDK here.
- name: Desktop issues
url: https://github.com/BelledonneCommunications/linphone-desktop/issues
about: Please post issues about the Desktop (Linux, MacOSX, Windows) application here.
- name: iOS issues
url: https://github.com/BelledonneCommunications/linphone-iphone/issues
about: Please post issues about the iPhone application here.
- name: Contacts
url: https://www.linphone.org/contact
about: For any contacts like commercial, licensing, mailing-lists

39
.gitignore vendored
View file

@ -1,29 +1,14 @@
*.orig
*.rej
.DS_Store
.gradle
.idea
.settings
adb.pid
bc-android.keystore
build
*.iml *.iml
lint.xml .gradle
/local.properties
.DS_Store
/build
/captures
.externalNativeBuild
.cxx
local.properties local.properties
res/.DS_Store app/debug/
res/raw/lpconfig.xsd app/release/
.d .idea/
.*clang* app/bc-android.keystore
**/*.iml .kotlin/
**/.classpath
**/.project
**/*.kdev4
**/.vscode
res/value-hi_IN
linphone-sdk-android/*.aar
app/debug
app/release
app/releaseAppBundle
app/releaseWithCrashlytics
keystore.properties
app/src/main/res/xml/contacts.xml

View file

@ -2,15 +2,15 @@ job-android:
stage: build stage: build
tags: [ "docker-android" ] tags: [ "docker-android" ]
image: gitlab.linphone.org:4567/bc/public/linphone-android/bc-dev-android:20220609_android_33 image: gitlab.linphone.org:4567/bc/public/linphone-android/bc-dev-android:20230414_bullseye_jdk_17_cleaned
before_script: before_script:
- if ! [ -z ${SCP_PRIVATE_KEY+x} ]; then eval $(ssh-agent -s); fi - if ! [ -z ${SCP_PRIVATE_KEY+x} ]; then eval $(ssh-agent -s); fi
- if ! [ -z ${SCP_PRIVATE_KEY+x} ]; then echo "$SCP_PRIVATE_KEY" | tr -d '\r' | ssh-add - > /dev/null; fi - if ! [ -z ${SCP_PRIVATE_KEY+x} ]; then echo "$SCP_PRIVATE_KEY" | tr -d '\r' | ssh-add - > /dev/null; fi
- echo "$ANDROID_SETTINGS_GRADLE" > settings.gradle - if ! [ -z ${ANDROID_SETTINGS_GRADLE+x} ]; then echo "$ANDROID_SETTINGS_GRADLE" > settings.gradle.kts; fi
- git config --global --add safe.directory /builds/BC/public/linphone-android
script: script:
- sdkmanager
- scp -oStrictHostKeyChecking=no $DEPLOY_SERVER:$ANDROID_KEYSTORE_PATH app/ - scp -oStrictHostKeyChecking=no $DEPLOY_SERVER:$ANDROID_KEYSTORE_PATH app/
- scp -oStrictHostKeyChecking=no $DEPLOY_SERVER:$ANDROID_GOOGLE_SERVICES_PATH app/ - scp -oStrictHostKeyChecking=no $DEPLOY_SERVER:$ANDROID_GOOGLE_SERVICES_PATH app/
- echo storePassword=$ANDROID_KEYSTORE_PASSWORD > keystore.properties - echo storePassword=$ANDROID_KEYSTORE_PASSWORD > keystore.properties
@ -18,15 +18,15 @@ job-android:
- echo keyAlias=$ANDROID_KEYSTORE_KEY_ALIAS >> keystore.properties - echo keyAlias=$ANDROID_KEYSTORE_KEY_ALIAS >> keystore.properties
- echo storeFile=$ANDROID_KEYSTORE_FILE >> keystore.properties - echo storeFile=$ANDROID_KEYSTORE_FILE >> keystore.properties
- ./gradlew app:dependencies | grep org.linphone - ./gradlew app:dependencies | grep org.linphone
- ./gradlew clean
- ./gradlew assembleDebug - ./gradlew assembleDebug
- ./gradlew assembleRelease - ./gradlew assembleRelease
artifacts: artifacts:
paths: paths:
- ./app/build/outputs/apk/debug/linphone-android-debug-*.apk
- ./app/build/outputs/apk/release/linphone-android-release-*.apk - ./app/build/outputs/apk/release/linphone-android-release-*.apk
when: always when: always
expire_in: 1 week expire_in: 1 day
.scheduled-job-android: .scheduled-job-android:

View file

@ -1,12 +1,20 @@
job-android-upload: job-android-upload:
stage: deploy stage: deploy
tags: [ "deploy" ] tags: [ "docker-deploy" ]
only: only:
- schedules - schedules
dependencies: dependencies:
- job-android - job-android
before_script:
- if ! [ -z ${SCP_PRIVATE_KEY+x} ] && ! [ -z ${DEPLOY_SERVER_HOST_KEYS+x} ]; then eval $(ssh-agent -s); fi
- if ! [ -z ${SCP_PRIVATE_KEY+x} ]; then echo "$SCP_PRIVATE_KEY" | tr -d '\r' | ssh-add - > /dev/null; fi
- if ! [ -z ${DEPLOY_SERVER_HOST_KEYS+x} ]; then mkdir -p ~/.ssh && chmod 700 ~/.ssh; fi
- if ! [ -z ${DEPLOY_SERVER_HOST_KEYS+x} ]; then echo "$DEPLOY_SERVER_HOST_KEYS" >> ~/.ssh/known_hosts; fi
script: script:
- cd app/build/outputs/apk/ && rsync ./debug/*.apk $DEPLOY_SERVER:$ANDROID_DEPLOY_DIRECTORY # Launches rsync in partial mode, which means that we are using a temp_dir in case of a transfer issue
# Upon a job relaunch, the files in temp_dir would then be re-used, and deleted if the transfer succeeds
- cd app/build/outputs/apk/ && rsync --partial --partial-dir=$CI_PIPELINE_ID_$CI_JOB_NAME ./release/*.apk $DEPLOY_SERVER:$ANDROID_DEPLOY_DIRECTORY

3
.idea/.gitignore generated vendored Normal file
View file

@ -0,0 +1,3 @@
# Default ignored files
/shelf/
/workspace.xml

1
.idea/.name generated Normal file
View file

@ -0,0 +1 @@
Linphone

123
.idea/codeStyles/Project.xml generated Normal file
View file

@ -0,0 +1,123 @@
<component name="ProjectCodeStyleConfiguration">
<code_scheme name="Project" version="173">
<JetCodeStyleSettings>
<option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
</JetCodeStyleSettings>
<codeStyleSettings language="XML">
<option name="FORCE_REARRANGE_MODE" value="1" />
<indentOptions>
<option name="CONTINUATION_INDENT_SIZE" value="4" />
</indentOptions>
<arrangement>
<rules>
<section>
<rule>
<match>
<AND>
<NAME>xmlns:android</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>xmlns:.*</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
<order>BY_NAME</order>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*:id</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*:name</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>name</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>style</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
<order>BY_NAME</order>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
<order>ANDROID_ATTRIBUTE_ORDER</order>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>.*</XML_NAMESPACE>
</AND>
</match>
<order>BY_NAME</order>
</rule>
</section>
</rules>
</arrangement>
</codeStyleSettings>
<codeStyleSettings language="kotlin">
<option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
</codeStyleSettings>
</code_scheme>
</component>

5
.idea/codeStyles/codeStyleConfig.xml generated Normal file
View file

@ -0,0 +1,5 @@
<component name="ProjectCodeStyleConfiguration">
<state>
<option name="USE_PER_PROJECT_SETTINGS" value="true" />
</state>
</component>

6
.idea/compiler.xml generated Normal file
View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="CompilerConfiguration">
<bytecodeTargetLevel target="17" />
</component>
</project>

9
.idea/misc.xml generated Normal file
View file

@ -0,0 +1,9 @@
<project version="4">
<component name="ExternalStorageConfigurationManager" enabled="true" />
<component name="ProjectRootManager" version="2" languageLevel="JDK_17" default="true" project-jdk-name="temurin-17" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/build/classes" />
</component>
<component name="ProjectType">
<option name="id" value="Android" />
</component>
</project>

6
.idea/vcs.xml generated Normal file
View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$" vcs="Git" />
</component>
</project>

View file

@ -10,7 +10,671 @@ Group changes to describe their impact on the project, as follows:
Fixed for any bug fixes. Fixed for any bug fixes.
Security to invite users to upgrade in case of vulnerabilities. Security to invite users to upgrade in case of vulnerabilities.
## [4.7.0] - Unreleased ## [6.1.0] - Unreleased
### Added
- Added the ability to edit/delete chat messages sent less than 24 hours ago.
- Added keyboard shortcuts on IncomingCallFragment: Ctrl + Shift + A to answer the call, Ctrl + Shift + D to decline it
- Added seeking feature to recordings & media player within app
- Added PDF preview in conversation (message bubble & documents list)
- Added hover effect when using a mouse (useful for tablets or devices with desktop mode)
- Support right click on some items to open bottom sheet/menu
- Added toggle speaker action in active call notification
- Increased text size for chat messages that only contains emoji(s)
- Use user-input to filter participants list after typing "@" in conversation send area
- Handle read-only CardDAV address books, disable edit/delete menus for contacts in read-only FriendList
- Added swipe/pull to refresh on contacts list of a CardDAV addressbook has been configured to force the synchronization
- Show information to user when filtering contacts doesn't show them all and user may have to refine it's search
- Show Android notification when an account goes to failed registration state (only when background mode is enabled)
- New settings:
- one for user to choose whether to sort contacts by first name or last name
- one to hide contacts that have neither a SIP address nor a phone number
- one to let app auto-answer call with video sending already enabled
- one to let edit native contacts Linphone copy in-app instead of opening native addressbook third party app
- Added a vu meter for recording & playback volumes (must be enabled in developer settings)
- Added support for HDMI audio devices
### Changed
- No longer follow TelecomManager audio endpoint during calls, using our own routing policy
- Join a conference using default layout instead of audio only when clicking on a meeting SIP URI
- Removing an account will also remove all related data in the local database (auth info, call logs, conversations, meetings, etc...)
- Hide SIP address/phone number picker dialog if contact has exactly one SIP address matching both the app default domain & the currently selected account domain
- Hide SIP address associated to phone number through presence mecanism in contact details & editor views.
- Improved UI on tablets with screen sw600dp and higher, will look more like our desktop app
- Improved navigation within app when using a keyboard
- Now loading media/documents contents in conversation by chunks (instead of all of them at once)
- Simplified audio device name in settings
- Reworked some settings (moved calls related ones from advanced settings to advanced calls settings)
- Increased shared media preview size in chat
- Un-encrypted conversation warning will be more visible for accounts that support end-to-end encrypted conversations
- Made numpad buttons larger by changing their shape
- All LDAP fields are mandatory now
- Improved how Android shortcuts are created
- Permission fragment will only show missing ones
- Added more info into StartupListener logs
- Updated password forgotten procedure, will use online account manager platform
### Fixed
- Copy raw message content instead of modified one when it contains a participant mention ("@username")
## [6.0.21] - 2025-12-16
### Added
- Allow linphone-config: scheme URIs in in-app QR code scanner
### Changed
- Workaround for audio focus & audio manager mode on devices that do not support TelecomManager APIs
- Set front camera as default after using back camera when scanning a QR code
- Added back largeHeap flag in AndroidManifest.xml
### Fixed
- Fixed call recording indicator not showing local record in progress in case UPDATE isn't answered
- Fixed native addressbook reload when a contact is updated in the OS default app
- Fixed issue with linphone-config scheme URIs if scheme is followed by "//"
- Fixed Job & Company contact field not updated if field content was removed
- Fixed local avatar not displayed when calling ourselves
- Prevent crashes due to some ActivityNotFound exceptions
- Prevent crash due to empty clipboard on some devices
## [6.0.20] - 2025-11-21
### Changed
- Added shrink resources to release config in gradle
### Fixed
- Remove AuthInfo when configuring a CardDAV friend list if synchronization fails
- Added missing toast when starting a group call or meeting if there's an issue
- Fixed crash in RecordingPlayerFragment due to used lateinit property before it's initialized
## [6.0.19] - 2025-10-16
### Added
- Spanish and Slovakian translations thanks to Weblate contributors
### Changed
- SIP addresses domain hidden in Suggestions if it matches the currently selected account SIP identity domain
- Start proximity sensor when an incoming call is answered from the notification (disabling screen when device is near)
### Fixed
- Black screen when trying to scan a QR Code right after granting CAMERA permission (only happened on some devices)
- Possible crash due to ConcurrentModificationException
- Camera preview in conference that was black sometimes after switching layout
- Possibly wrong screen sharing participant name in conference
- Presence SUBSCRIBE that was only sent for sip.linphone.org accounts
- Keyboard suggestions in participant picker textfield
- Account labelled as Disabled instead of Disconnected when network isn't reachable
- Suggestions generated avatar if username starts with '+'
- Two LDAP fields label where swapped
## [6.0.18] - 2025-09-15
### Added
- Added menu icon next to currently selected account avatar to make the drawer menu easier to understand
- Added missing dialpad floating action button in the call transfer fragment
### Changed
- Improved bodyless friendlist presence process when it's received
### Fixed
- Fixed "End-to-end encrypted call" label while in conference, the call may be end-to-end encrypted but only to the conference server, not to all participants
- Fixed missing meeting subject when calling the conference SIP URI if the conference info doesn't exist yet
- Finish CallActivity if no call is found when trying to answer/decline a call from the IncomingCallFragment
- Prevent empty screen when rotating the device and clicking on the empty part next to the list while in landscape and then rotating the device back to portrait
## [6.0.17] - 2025-09-02
### Changed
- Portuguese translation updated from Weblate (still not complete)
### Fixed
- Vibrator not stopped when call is terminated sometimes (SDK fix)
- Chat conversation not visible sometimes (SDK fix)
## [6.0.16] - 2025-08-25
## Added
- Access to Help/Troubleshooting pages from Assistant
## Fixed
- Some Core methods being called from UI thread causing either a crash or a deadlock sometimes
- Scrolling issue when doing a search in a conversation with only one result
- Contacts not updated after body less presence notify was received
- VFS issue due to encrypted.pref file being backed up by Android OS
## [6.0.15] - 2025-08-11
### Fixed
- Crash due to changes in SDK triggering fatal error if linphone_core_stop() is called from linphone_core_iterate() loop (which was done when scanning QR code)
### Changed
- Prevent leaving assistant after doing a remote provisioning if there is still no account after it (if there was no account before and no account was provided in downloaded config)
## [6.0.14] - 2025-08-06
### Fixed
- Fixed ANR due to deadlock caused by method being called from wrong thread
- Fixed microphone not always recording audio while app in background or if screen is turned off
- Fixed missing favorites in start call / create conversation views
- Fixed outgoing call view in full screen
- Fixed generated avatar for SIP URIs without username
## [6.0.13] - 2025-07-31
### Fixed
- Missing favourites if contacts list size exceeds magic search max results setting
- Muted call on some devices due to Telecom Manager quickly muting/unmuting call
- Full screen without video during outgoing early media call if video has been declined by remote end
- Removed duplicated week label if "no meeting today" is the first entry for current week
- Prevent crash during file export if no app on the device can handle it
- Prevent crash that could happen with chat message notification if sender name (or group chat room subject) is empty
### Changed
- Back gesture / navigation button will close the numpad bottom sheet if it's open instead of leaving the page directly
- Updated bell and bell_slash icons
## [6.0.12] - 2025-07-18
### Fixed
- Reactions list in bottom sheet update while opened
- Crashes due to late init properties being used before initialized
## [6.0.11] - 2025-07-11
### Added
- Added toggle in LDAP configuration to allow to quickly enable/disable it
### Changed
- Reduced maximum number of contacts displayed in contacts list, new call/conversation, meeting participant selection etc...
- Updated translations
### Fixed
- Calls top bar wrong notification label when going from two calls to one.
## [6.0.10] - 2025-06-27
### Added
- Added a new top bar alert area for pending file/text sharing.
### Changed
- Reworked in-app top bar alerts, now can show both an account alert and an active call alert.
- Hide SIP address/phone number picker dialog if contact has exactly one SIP address matching the default domain and currently default account domain.
### Fixed
- Bluetooth not being used automatically when device is connected during a call.
- Call encryption status label stuck in "Waiting for encryption".
- Group chat room creation if LIME server URL isn't set.
- Participant mention if more than one in the same chat message.
- Force default account in call params when starting one.
## [6.0.9] - 2025-06-06
### Added
- German translation (88% complete)
- Link to user guide in Help section
- Missing scroll views for help & debug layouts
### Changed
- Prevent port from being set in the SIP identity address in third party account login + remove port (if any) from SIP identity for existing accounts
- Show last message timestamp instead of conversation last updated timestamp in conversations list
### Fixed
- Prevent blinking in conversations list when removing message from chat room
- Prevent empty (can even lead to crash) display name in call notification (using all identification fields from vCard)
## [6.0.8] - 2025-05-23
### Added
- Ukrainian & simplified Chinese translations from Weblate
- Sliding answer/decline button in incoming call fragment if device is locked (will help prevent calls from being unintentionally picked up or hung up while the device is being removed from a pocket)
### Changed
- Show files with square design when more than one (as it is for media files)
- Outgoing chat bubbles will now display the sent file size (as it is for received messages)
### Fixed
- Fixed issue with bluetooth hearing aids
- Fixed audio call being answered on speakerphone
- Fixed events related to joined/left conversation being briefly visible sometimes for 1-1 conversations
- Fixed files/media grid in chat bubble using more than 3 columns in landscape
- Fixed logs upload server URL setting
## [6.0.7] - 2025-05-16
### Added
- CS, NL and RU translations from Weblate
### Changed
- Improved find contact performances
- Make sure speaker audio device is used for playing the ringtone during early media
- Reworked bottom navigation bar in portrait and unread count indicators
- No longer delete conversations when deleting account (for now); causes user to leave group which is an issue when using multiple devices
### Fixed
- Fixed no default account after remote provisioning
- Prevent lists from refreshing too many times when using LDAP or remote CardDAV contact directories
- Fixed black miniatures in conference if bundle mode is disabled in account params
- Fixed long press on a chat message containing a SIP URI triggering call
- Disable IMDN bottom sheet for incoming messages in groups instead of showing it empty
- Refresh conversations list after clearing conversation history
- Fixed another race condition issue related to foreground call service
## [6.0.6] - 2025-05-02
### Added
- Added recover phone account when clicking on "Forgotten password" in the assistant
- Improved message when contacts list is empty depending on the currently selected filter and added a button to open the filter popup menu for users that didn't notice the icon on the top right corner of the screen when contacts list is empty and "SIP contacts only" filter is set.
- Added "Logs collection sharing server URL" setting in developper area
- Added "Disable sending logs to Crashlytics" advanced setting.
### Changed
- Improved VFS message in confirmation dialog
- Moved "Print logs in logcat" and "File sharing server URL" settings to developper area
### Fixed
- Fixed crash when opening a password protected PDF
- Fixed chat room lookup while in 1-1 call, using SDK method for getting chat room from conference
- Fixed newly created contact not being visible in contacts list without reloading it
- Fixed missing event icon for group conversations
- Another attempts at preventing crashes due to In-Call service not being started as foreground before being stopped
## [6.0.5] - 2025-04-18
### Changed
- When calling a SIP URI that looks like a phone number in the username and an IP in the domain, replace the domain with the one of the currently selected account to workaround issue with PBXs using IPs instead of domains in From header
- Improved account creation page UI when push notifications aren't available
- Improved called account display on incoming call screen when more than one account configured
- Updated telecom package from beta to release candidate
### Fixed
- Fixed transfer call view numpad button starting a new call instead of forwarding the current one
- Fixed incoming call not displayed in call history depending on how the From & To headers are formatted (SDK fix)
- Fixed crashes related to foreground service not being started
- Fixed crash due to lateinit property not being initialized before used
## [6.0.4] - 2025-04-11
### Changed
- Third party SIP accounts push notifications will be disabled and setting will be hidden unless if list of supported domains (to prevent issues, specifically when used with UDP transport protocol causing bigger packets getting lost)
### Fixed
- Prevent refresh of views due to contacts changes to happen to frequently at startup
- Prevent crash in Help view if app is built without Firebase
## [6.0.3] - 2025-04-04
### Added
- Show alert when default account is disabled
- Refesh list details when going back from background after one hour or more (when keep app alive using service is enabled)
- Click to copy SIP URI in call history shortcut
- Added developper settings, must click 8 times on version (in Help) to make it appear (E2E encryption for meetings & group calls setting was moved there)
- Circular indicator while search is in progress in contacts lists
### Changed
- Force some default values on notifications channels
- Contacts list filter is now applied to new call / conversation & other contact pickers
- Attach file icon stays visible while typing message in conversation instead of emoji picker icon
### Fixed
- No default account being selected if the default one is removed
- Navigation bar turning orange when opening search bar
- Incoming call showed as video even if video is disabled locally
- Concurrent modification crash in Contacts loader
- Meetings list not properly sorted when CCMP is used
- POST_NOTIFICATIONS permission check on old Android devices
## [6.0.2] - 2025-03-28
### Added
- Show on top bar if FULL_SCREEN_INTENT permission isn't granted, clicking on it sends to the matching settings so user can fix it easily, without it incoming call screen won't be displayed if screen is off
- Ring during incoming early media call setting added back
- Added a floating action button to open dialpad during outgoing early media call
### Changed
- Delete all related call history / conversations / meetings when removing an account
- Delay / use a separated thread for heavy contacts related tasks to ensure call is correctly handled and foreground service is started quickly enough
- Newly created account in app will be kept disabled until SMS code validation is done
- Keep app alive foreground service notification no shows a content message to ease clicking on it to open the app & workaround a crash on some devices
- Automatically show dialpad setting will now also work on new / transfer call while in call as well
### Fixed
- Improved POST_NOTIFICATIONS permission check on Android 13 and newer, should prevent crashes
- Fixed contact lookup if phone number starts by "00" instead of "+"
- Fixed "delete all call history" sometimes not removing all call logs
- Fixed LDAP / remote CardDAV contacts sometimes not displayed in contacts list when doing a search
- Fixed issue where contact filter could be set to only show sip.linphone.org contacts even when third party account was being selected
- Fixed sometimes wrong displayed SIP URI in detailed call history
- Fixed invisible meeting icon in status bar
- Fixed missed call count indicator behavior with some third party providers
- Prevent today indicator & meeting icon in bottom nav bar from blinking / briefly appearing
- Fixed bottom nav bar sometimes being hidden
- Fixed missing share logs server URL when migrating from 5.2 if that value was removed back then
- Other crashes fixed
## [6.0.1] - 2025-03-21
### Added
- Start at boot & auto answer settings added back
- Interface setting to have dialpad automatically opened in start call view
- Replace "+" by "00" and do not apply prefix for calls & chat account settings
- Setting to let user choose whether to record calls using MKV or SMFF format (the later allows to record H265/AV1 video but is a proprietary file format that can't be read outside of Linphone)
### Changed
- Reverted the way of playing incoming call ringone (you may have to configure your own ringtone again), was causing various issues depending on devices/firmwares
- Show all call history entries if only one account is configured (workaround for missing history for now until a proper fix will be done in SDK)
### Fixed
- Issue preventing bluetooth Hearing Aids from working properly (and fixed earpiece/hearing aids icon)
- Prevent Qr Code scanner to use static picture camera
- Prevent user from connecting the same account multiple times
- Quit menu visibility not updated when changing Keep Alive setting
- Participant selection in group when typing "@"
- Recordings order has been reversed to have newest ones at top
- Improved message when network is not reachable due to "Wifi only mode" being enabled
- Various crash & bug fixes
## [6.0.0] - 2025-03-11
6.0.0 release is a complete rework of Linphone Android, with a fully redesigned UI, so it is impossible to list everything here.
### Changed
- Separated threads: Contrary to previous versions, our SDK is now running in it's own thread, meaning it won't freeze the UI anymore in case of heavy work, thus reducing the number of ANR and greatly increasing the fluidity of the app.
- Asymmetrical video : you no longer need to send your own camera feed to receive the one from the remote end of the call, and vice versa.
- Improved multi account: you'll only see history, conversations, meetings etc... related to currently selected account, and you can switch the default account in two clicks.
- Call transfer: Blind & Attended call transfer have been merged into one: during a call, if you initiate a transfer action, either pick another call to do the attended transfer or select a contact from the list (you can input a SIP URI not already in the suggestions list) to start a blind transfer.
- User can only send up to 12 files in a single chat message.
- IMDNs are now only sent to the message sender, preventing huge traffic in large groups, and thus the delivery status icon for received messages is now hidden in groups (as it was in 1-1 conversations).
- Settings: a lot of them are gone, the one that are still there have been reworked to increase user friendliness.
- Default screen (between contacts, call history, conversations & meetings list) will change depending on where you were when the app was paused or killed, and you will return to that last visited screen on the next startup.
- Gradle files have been migrated from Groovy to Kotlin DSL, and dependencies are now in a separated file (libs.versions.toml).
- Account creation no longer allows you to use your phone number as username, but it is still required to provide it to receive activation code by SMS.
- Minimum supported Android OS version is now 9 (API level 28).
- Telecom Manager support is now based on androidx.core.core-telecom package.
- Some settings have changed name and/or section in linphonerc file.
### Added
- Contacts trust: contacts for which all devices have been validated through a ZRTP call with SAS exchange are now highlighted with a blue circle (and with a red one in case of mistrust). That trust is now handled at contact level (instead of conversation level in previous versions).
- Media & documents exchanged in a conversation can be easily found through a dedicated screen.
- A brand new chat message search feature has been added to conversations.
- You can now react to a chat message using any emoji.
- If next message is also a voice recording, playback will automatically start after the currently playing one ends.
- Chat while in call: a shortcut to a conversation screen with the remote.
- Chat while in a conference: if the conference has a text stream enabled, you can chat with the other participants of the conference while it lasts. At the end, you'll find the messages history in the call history (and not in the list of conversations).
- Auto export of media to native gallery even when auto download is enabled (but still not if VFS is enabled nor for ephemeral messages).
- Save / export document & media from ephemeral messages will be disabled, and secure policy that prevents screenshots will be enforced in file viewer even if the setting is disabled.
- Notification showing upload/download of files shared through chat will let user know the progress and keep the app alive during that process.
- Screen sharing in conference: only desktop app starting with 6.0 version is able to start it, but on mobiles you'll be able to see it.
- You can choose whatever ringtone you'd like for incoming calls (in Android notification channel settings).
- Security focus: security & trust is more visible than ever, and unsecure conversations & calls are even more visible than before.
- CardDAV: you can configure as many CardDAV servers you want to synchronize you contacts in Linphone (in addition or in replacement of native addressbook import).
- OpenID: when used with a SSO compliant SIP server (such as Flexisip), we support single-sign-on login.
- MWI support: display and allow to call your voicemail when you have new messages (if supported by your VoIP provider and properly configured in your account params).
- CCMP support: if you configure a CCMP server URL in your accounts params, it will be used when scheduling meetings & to fetch list of meetings you've organized/been invited to.
- Devices list: check on which device your sip.linphone.org account is connected and the last connection date & time (like on subscribe.linphone.org).
- Protobuf dependency to allow logging native crashes stack traces at next app startup.
- Android 15 startup listener, allowing us to log type of start (cold, warm, etc...) and some other useful info.
- Dialer & in-call numpad show letters under the digit.
### Removed
- Dialer: the previous home screen (dialer) has been removed, you'll find it as an input option in the new start call screen.
- Peer-to-peer: a SIP account (sip.linphone.org or other) is now required.
- Contacts: we no longer add contacts created in-app in the native addressbook (WRITE_CONTACTS permission was removed), but we still import them if you grant us the READ_CONTACTS permission.
### Fixed
- No longer trying to play vocal messages & call recordings using bluetooth when connected to an Android Auto car, causing playback issues.
- AAudio driver no longer causes delay when switching between devices (SDK fix).
## [5.2.5] - 2024-05-03
### Changed
- Updated translations
## [5.2.4] - 2024-04-22
### Fixed
- Active speaker video hidden when you are the first one to join a meeting
- Show camera icon instead of microphone for incoming video calls
- SIP URI parsing from native contact due to international prefix being applied when it shouldn't
- Various fixes for broadcast mode
## [5.2.3] - 2024-01-31
### Fixed
- Crash due to OOM for some images sent/received in chat
- Crash while navigating to account settings
### Changed
- Updated translations (Romanian, Polish, Portuguese)
## [5.2.2] - 2024-01-15
### Fixed
- Local conference created my merging audio streams
## [5.2.1] - 2023-12-23
### Fixed
- Crash when Service starts before CoreContext
## [5.2.0] - 2023-12-21
### Added
- Chat messages emoji "reactions"
- Hearing aids should be working the same way bluetooth headset does
- Hardware video codecs (H264, H265) are now used in priority when possible (SDK)
- Broadcast mode for scheduled meetings (hidden)
- Android 14 support
### Changed
- BLUETOOTH_CONNECT permission is no longer required
### Fixed
- Correctly switching to either bottom or back microphone depending on wether the earpiece or the speaker is used,
and also use the same device for input and output if the one set as output as RECORD capability
(fixes echo issue while on speakerphone on some devices such as Samsung's)
- Connection status & color when in refreshing state
- Sent content type for files attached to a chat message
- Toggle mute mic while in conference
- Calling right after creating a chat room
## [5.1.4] - 2023-10-20
### Fixed
- Various fixes in the SDK (5.2.110)
### Changed
- Updated translations from Weblate
## [5.1.3] - 2023-09-23
### Fixed
- Core not able to open database due to issue in 5.2.107 SDK from last update
- Incoming call activity and lock screen interaction
- Selected "meeting" filter icon color
## [5.1.2] - 2023-09-22
### Added
- Italian translation completed
### Fixed
- Multiple authentication requested dialogs stacking above each other sometimes
- Downgraded navigation version to try to prevent some crashes reported on the Play Store
## [5.1.1] - 2023-09-06
### Fixed
- Fixed issue in SDK randomly generated password when creating account from app
- Various issues reported on the Play Store
## [5.1.0] - 2023-08-21
### Added
- Showing short term presence for contacts whom publish it + added setting to disable it (enabled by default for sip.linphone.org accounts)
- Confirmation dialog before removing account
- Attended transfer instead of blind transfer if there is more than 1 call
- Last sent message delivery status (IMDN) icon in chat rooms list
- Emoji picker in chat room, and increase size of text if it only contains emojis
- Hidden setting to disable video completely
- Hidden setting to prevent adding / editing / removing native contacts
- Hidden setting to protect settings access using account password
- SIP URI in call can be selected using long press
- Dialog showing up asking for correct account password in case of failed authentication
### Changed
- Switched Account Creator backend from XMLRPC to FlexiAPI, it now requires to be able to receive a push notification
- Email account creation form is now only available if TELEPHONY feature is not available, not related to screen size anymore
- Replaced voice recordings file name by localized placeholder text, like for video conferences invitations
- Decline incoming calls with Busy reason if there is at least another active call
- Open keyboard when replying to a message if no text / file / voice record is pending
- Removed jetifier as it is not needed
- Switched from gradle 7.5 to 8.0, requires JDK 17 (instead of 11)
### Fixed
- Messages not marked as reply in basic chat room if sending more than 1 content
- Chat message video attachment display when failing to get a preview picture
## [5.0.14] - 2023-06-20
### Changed
- SDK update only
## [5.0.13] - 2023-06-15
### Changed
- SDK update only
## [5.0.12] - 2023-05-23
### Fixed
- Crash if notification manager throws an exception
- Video preview not moving if call was started in audio only
## [5.0.11] - 2023-05-09
### Fixed
- Wrong call displayed when hanging up a call while an incoming one is ringing
- Crash related to call history
- Crash due to wrongly format string
- Add/remove missing listener on FriendLists created after Core has been created
### Changed
- Improved GSM call interruption
- Updated translations
## [5.0.11] - 2023-05-09
### Fixed
- Wrong call displayed when hanging up a call while an incoming one is ringing
- Crash related to call history
- Crash due to wrongly format string
- Add/remove missing listener on FriendLists created after Core has been created
### Changed
- Improved GSM call interruption
- Updated translations
## [5.0.10] - 2023-04-04
### Fixed
- Plain copy of encrypted files (when VFS is enabled) not cleaned
- Avatar display issue if contact's "initials" contains more than 1 emoji or an emoji + a character
## [5.0.9] - 2023-03-30
### Fixed
- Admin weren't visible for non admin users in group chat rooms
- Crash when clicking on URI in chat if not matching app is found on Android to handle it
- LIME update threshold wasn't set, causing a request to be made after each REGISTER
### Changed
- Now SDK automatically handles TextureView's listener, removed it from app
- Bumped license year to 2023
- Force remove LIME X3DH server URL for third party accounts
## [5.0.8] - 2023-03-20
### Fixed
- Trying to prevent crash in call history
- Color icon in dark mode in chat for files & replies
### Changed
- Updated translations
## [5.0.7] - 2023-02-27
### Fixed
- Fixed navigating to a contact that doesn't have a native ID, but using it's SIP address instead
- Fixed account creator resolved country name & create button not enabled
### Changed
- Updated translations
## [5.0.6] - 2023-02-17
### Fixed
- Wrong country displayed in assistant after picking it in the list if another country has the same international prefix (such as +1)
- SIP URI clickable pattern missing '~'
- Crash that happens sometimes when CallActivity is destroyed
- Pressing send message button while recording a voice message not sending it
- Missing ephemeral icon next to send message icon
- Headers colors in IMDN details
- Pixel issue in call quality indicator 2 icon
### Changed
- Improved incoming call layout when receiving early-media video
- Hidden "Echo Tester" setting unless in debug mode as it can mislead user and isn't useful for end user
## [5.0.5] - 2023-01-19
### Fixed
- Issue with how replies where added to chat message notification from reply action
## [5.0.4] - 2023-01-18
### Added
- Show a progress bar while importing files to the chat sending area
### Changed
- Prevent keyboard from auto-replacing some user input such as username, breaking SIP URIs unknowingly
### Fixed
- Prevent copy of files that weren't sent in chat to be kept in app local folder
## [5.0.3] - 2023-01-13
### Added
- Voice message recording/playback will use bluetooth/headset/headphones/hearing aid device if available
- Chat message notifications are now compatible with Android Auto
### Changed
- In video conference, when in active speaker layout, currently speaking participant miniature will be hidden
- Attach file, voice recording and send message icons are now a bit bigger
- Updated Firebase BoM, gradle & some dependencies
### Fixed
- ANR happening sometimes during voice message playback
## [5.0.2] - 2023-01-05
### Changed
- Export files to native gallery is now available even if automatically download files setting is enabled
### Fixed
- Makes sure sip.linphone.org accounts have a LIME X3DH server URL for E2E chat messages encryption
- Files not being exported to native gallery sometimes
- Crashes reported by Google Play Store & Crashlytics
## [5.0.1] - 2022-12-16
### Changed
- File transfer progress indication & error status improvements
### Fixed
- Wrong LIME status for participant that has multiple devices
- No longer sends video when switching from audio only to another conference layout
- SIP URI regex pattern to prevent HTTP URLs containing '@' to be handled as SIP URI
## [5.0.0] - 2022-12-06
### Added ### Added
- Post Quantum encryption when using ZRTP - Post Quantum encryption when using ZRTP
@ -28,6 +692,7 @@ Group changes to describe their impact on the project, as follows:
### Changed ### Changed
- In-call views have been re-designed - In-call views have been re-designed
- "Media Encryption Mandatory" setting now allows for any media encryption (instead of only the one selected in the above setting previously)
- Improved how call logs are handled to improve performances - Improved how call logs are handled to improve performances
- Improved how contact avatars are generated - Improved how contact avatars are generated
- 3-dots menu even for basic chat rooms with more options - 3-dots menu even for basic chat rooms with more options
@ -42,10 +707,13 @@ Group changes to describe their impact on the project, as follows:
- Show service notification sooner to prevent crash if Core creation takes too long - Show service notification sooner to prevent crash if Core creation takes too long
- Incoming call screen not being showed up to user (& screen staying off) when using app in Samsung secure folder - Incoming call screen not being showed up to user (& screen staying off) when using app in Samsung secure folder
- One to one chat room creation process waiting indefinitely if chat room already exists - One to one chat room creation process waiting indefinitely if chat room already exists
- Contact edition (SIP addresses & phone numbers) not working due to original value being lost in Friend parsing
- Automatically start call recording
- "Blinking" in some views when presence is being received - "Blinking" in some views when presence is being received
- Trying to keep the preferred driver (OpenSLES / AAudio) when switching device - Trying to keep the preferred driver (OpenSLES / AAudio) when switching device
- Issues when storing presence in native contacts + potentially duplicated SIP addresses in contact details - Issues when storing presence in native contacts + potentially duplicated SIP addresses in contact details
- Chat room scroll position lost when going into sub-view - Chat room scroll position lost when going into sub-view
- Trim user input to remove any space at end of string due to keyboard auto completion
- No longer makes requests to our LIME server (end-to-end encryption keys server) for non sip.linphone.org accounts - No longer makes requests to our LIME server (end-to-end encryption keys server) for non sip.linphone.org accounts
- Fixed incoming call/notification not ringing if Do not Disturb mode is enabled except for favorite contacts - Fixed incoming call/notification not ringing if Do not Disturb mode is enabled except for favorite contacts

View file

@ -1,11 +1,12 @@
[![pipeline status](https://gitlab.linphone.org/BC/public/linphone-android/badges/master/pipeline.svg)](https://gitlab.linphone.org/BC/public/linphone-android/commits/master) [![weblate status](https://weblate.linphone.org/widgets/linphone/-/linphone-android/svg-badge.svg)](https://weblate.linphone.org/engage/linphone/?utm_source=widget) [![pipeline status](https://gitlab.linphone.org/BC/public/linphone-android/badges/master/pipeline.svg)](https://gitlab.linphone.org/BC/public/linphone-android/commits/master)
[![weblate status](https://weblate.linphone.org/widget/linphone/linphone-android-6-0/status-badge.png)](https://weblate.linphone.org/engage/linphone/)
Linphone is an open source softphone for voice and video over IP calling and instant messaging. Linphone is an open source softphone for voice and video over IP calling and instant messaging.
It is fully SIP-based, for all calling, presence and IM features. It is fully SIP-based, for all calling, presence and IM features.
General description is available from [linphone web site](https://www.linphone.org/technical-corner/linphone). General description is available from [linphone web site](https://linphone.org).
### How to get it ### How to get it
@ -21,11 +22,11 @@ Linphone is dual licensed, and is available either :
- under a [GNU/GPLv3 license](https://www.gnu.org/licenses/gpl-3.0.en.html), for free (open source). Please make sure that you understand and agree with the terms of this license before using it (see LICENSE file for details). - under a [GNU/GPLv3 license](https://www.gnu.org/licenses/gpl-3.0.en.html), for free (open source). Please make sure that you understand and agree with the terms of this license before using it (see LICENSE file for details).
- under a proprietary license, for a fee, to be used in closed source applications. Contact [Belledonne Communications](https://www.linphone.org/contact) for any question about costs and services. - under a proprietary license, for a fee, to be used in closed source applications. Contact [Belledonne Communications](https://linphone.org/contact) for any question about costs and services.
### Documentation ### Documentation
- Supported features and RFCs : https://www.linphone.org/technical-corner/linphone/features - Supported features and RFCs : https://www.linphone.org/linphone-softphone/#linphone-fonctionnalites
- Linphone public wiki : https://wiki.linphone.org/xwiki/wiki/public/view/Linphone/ - Linphone public wiki : https://wiki.linphone.org/xwiki/wiki/public/view/Linphone/
@ -33,16 +34,11 @@ Linphone is dual licensed, and is available either :
# What's new # What's new
App has been totally rewritten in Kotlin using modern components such as Navigation, Data Binding, View Models, coroutines, etc... 6.0.0 release is a completely new version, designed with UX/UI experts and marks a turning point in design, features, and user experience. The improvements make this version smoother and simpler for both developers and users.
Check the [CHANGELOG](./CHANGELOG.md) file for a more detailed list.
The first linphone-android release that will be based on this will be 4.5.0, using 5.0.0 SDK.
We're also taking a fresh start regarding translations so less languages will be available for a while. You can take a look at the [CHANGELOG.md](CHANGELOG.md) file for a non-exhaustive list of changes of this new version and of the newly added features, the most exciting ones being the improved fluidity, a real multi-accounts support and asymmetrical video in calls.
If you want to contribute, you are welcome to do so, check the [Translations](#Translations) section below.
org.linphone.legacy flavor (old java wrapper if you didn't migrate your app code to the new one yet) is no longer supported starting 5.0.0 SDK. This release only works on Android OS 9.0 and newer.
The sample project has been removed, we now recommend you to take a look at our [tutorials](https://gitlab.linphone.org/BC/public/tutorials/-/tree/master/android/kotlin).
# Building the app # Building the app
@ -97,6 +93,8 @@ LinphoneSdkBuildDir=/home/<username>/linphone-sdk/build/
## Known issues ## Known issues
- If you have the following build issue `AAPT: error: resource drawable/linphone_logo_tinted (aka org.linphone:drawable/linphone_logo_tinted) not found`, delete the `app/src/main/res/xml/contacts.xml` file (you can do it simply with `git clean -f` command) and start the build again.
- If you encounter the `couldn't find "libc++_shared.so"` crash when the app starts, simply clean the project in Android Studio (under Build menu) and build again. - If you encounter the `couldn't find "libc++_shared.so"` crash when the app starts, simply clean the project in Android Studio (under Build menu) and build again.
Also check you have built the SDK for the right CPU architecture using the `-DLINPHONESDK_ANDROID_ARCHS=armv7,arm64,x86,x86_64` cmake parameter. Also check you have built the SDK for the right CPU architecture using the `-DLINPHONESDK_ANDROID_ARCHS=armv7,arm64,x86,x86_64` cmake parameter.
@ -106,13 +104,9 @@ Also check you have built the SDK for the right CPU architecture using the `-DLI
### Behavior issue ### Behavior issue
When submitting an issue on our [Github repository](https://github.com/BelledonneCommunications/linphone-android), please follow the template and attach the matching library logs: When submitting an issue on our [Github repository](https://github.com/BelledonneCommunications/linphone-android), please follow the template and attach the matching library logs.
1. To enable them, go to Settings -> Advanced and toggle `Debug Mode`. If they are already enabled, clear them first using the `Reset logs` button on the About page. Starting 6.0.0 release, logs are always enabled and stored locally on the device, you can clear them/upload them securely on our server for sharing by going into the Help → Troubleshooting page.
2. Then restart the app, reproduce the issue and upload the logs using the `Send logs` button on the advanced settings page.
3. Finally paste the link to the uploaded logs (link is already in the clipboard after a successful upload).
### Native crash ### Native crash
@ -120,16 +114,13 @@ First of all, to be able to get a symbolized stack trace, you need the debug ver
If you haven't built the SDK locally (see [building a local SDK](#BuildingalocalSDK)), here's how to get them: If you haven't built the SDK locally (see [building a local SDK](#BuildingalocalSDK)), here's how to get them:
1. Go to our [maven repository](https://download.linphone.org/maven_repository/org/linphone/linphone-sdk-android-debug/), in the linphone-android-debug directory. 1. Go to our [maven repository](https://download.linphone.org/maven_repository/org/linphone/linphone-sdk-android/) and find the directory that matches the version of our SDK that crashed.
2. Download the AAR file with **the exact same version** as the AAR that was used to generate the crash's stacktrace. 2. Download the linphone-sdk-android-<version>-libs-debug.zip archive.
3. Extract the AAR somewhere on your computer (it's a simple ZIP file even it's doesn't have the extension). Libraries are stored inside the ```jni``` folder (a directory for each architectured built, usually ```arm64-v8a, armeabi-v7a, x86_64 and x86```). 3. Extract the symbolized libraries somewhere on your computer, it will create a ```libs-debug``` directory.
4. To get consistent with locally built SDK, rename the ```jni``` directory into ```libs-debug```.
Now you need the ```ndk-stack``` tool and possibly ```adb logcat```. Now you need the ```ndk-stack``` tool and possibly ```adb logcat```.
If your computer isn't used for Android development, you can download those tools from [Google website](https://developer.android.com/studio#downloads), in the ```Command line tools only``` section. If your computer isn't used for Android development, you can download those tools from [Google website](https://developer.android.com/studio#downloads), in the ```Command line tools only``` section.
Once you have the debug libraries and the proper tools installed, you can use the ```ndk-stack``` tool to symbolize your stacktrace. Note that you also need to know the architecture (armv7, arm64, x86, etc...) of the libraries that were used. Once you have the debug libraries and the proper tools installed, you can use the ```ndk-stack``` tool to symbolize your stacktrace. Note that you also need to know the architecture (armv7, arm64, x86, etc...) of the libraries that were used.
@ -144,14 +135,22 @@ adb logcat -d | ndk-stack -sym ./libs-debug/`adb shell getprop ro.product.cpu.ab
``` ```
Warning: This command won't print anything until you reproduce the crash! Warning: This command won't print anything until you reproduce the crash!
Starting [NDK r29](https://github.com/android/ndk/wiki/Changelog-r29) you will be able to directly use the ```libs-debug.zip``` file in ```ndk-stack -sym``` argument.
## Create an APK with a different package name ## Create an APK with a different package name
Before the 4.1 release, there were a lot of files to edit to change the package name. Simply edit the ```app/build.gradle.kts``` file and change the value of the ```packageName``` variable.
Now, simply edit the app/build.gradle file and change the value returned by method ```getPackageName()```
The next build will automatically use this value everywhere thanks to ```manifestPlaceholders``` feature of gradle and Android. The next build will automatically use this value everywhere thanks to ```manifestPlaceholders``` feature of gradle and Android.
You may have already noticed that the app installed by Android Studio has ```org.linphone.debug``` package name. We no longer build the debug flavor with a different package name, but if you still want that behavior you only have to change the value of ```useDifferentPackageNameForDebugBuild``` to ```true```. When enabled, app built and installed by Android studio will have ```org.linphone.debug``` package name instead of ```org.linphone```.
If you build the app as release, the package name will be ```org.linphone```.
If you encounter
```
Execution failed for task ':app:processDebugGoogleServices'.
> No matching client found for package name 'your package name'
```
error when building, make sure you have replaced the ```app/google-services.json``` file by yours (containing your package name).
If you don't have such file because you don't rely on Firebase Cloud Messaging features nor Crashlytics, delete the file instead.
## Firebase push notifications ## Firebase push notifications
@ -169,6 +168,10 @@ We no longer use transifex for the translation process, instead we have deployed
Due to the full app rewrite we can't re-use previous translations, so we'll be very happy if you want to contribute. Due to the full app rewrite we can't re-use previous translations, so we'll be very happy if you want to contribute.
<a href="https://weblate.linphone.org/engage/linphone/">
<img src="https://weblate.linphone.org/widget/linphone/linphone-android-6-0/multi-auto.svg" alt="Translation status" />
</a>
# CONTRIBUTIONS # CONTRIBUTIONS
In order to submit a patch for inclusion in linphone's source code: In order to submit a patch for inclusion in linphone's source code:

View file

@ -1,277 +0,0 @@
plugins {
id 'com.android.application'
id 'kotlin-android'
id 'kotlin-kapt'
id 'org.jlleitschuh.gradle.ktlint'
}
def appVersionName = "4.7.0"
// def appVersionCode = 40700
def appVersionCode = 40700
static def getPackageName() {
return "org.linphone"
}
def firebaseEnabled = new File(projectDir.absolutePath +'/google-services.json').exists()
def crashlyticsEnabled = new File(projectDir.absolutePath +'/google-services.json').exists() && new File(LinphoneSdkBuildDir + '/libs/').exists() && new File(LinphoneSdkBuildDir + '/libs-debug/').exists()
if (firebaseEnabled) {
apply plugin: 'com.google.gms.google-services'
}
if (crashlyticsEnabled) {
apply plugin: 'com.google.firebase.crashlytics'
}
def gitBranch = new ByteArrayOutputStream()
task getGitVersion() {
def gitVersion = appVersionName
def gitVersionStream = new ByteArrayOutputStream()
def gitCommitsCount = new ByteArrayOutputStream()
def gitCommitHash = new ByteArrayOutputStream()
try {
exec {
executable "git" args "describe", "--abbrev=0"
standardOutput = gitVersionStream
}
exec {
executable "git" args "rev-list", gitVersionStream.toString().trim() + "..HEAD", "--count"
standardOutput = gitCommitsCount
}
exec {
executable "git" args "rev-parse", "--short", "HEAD"
standardOutput = gitCommitHash
}
exec {
executable "git" args "name-rev", "--name-only", "HEAD"
standardOutput = gitBranch
}
if (gitCommitsCount.toString().toInteger() == 0) {
gitVersion = gitVersionStream.toString().trim()
} else {
gitVersion = gitVersionStream.toString().trim() + "." + gitCommitsCount.toString().trim() + "+" + gitCommitHash.toString().trim()
}
println("Git version: " + gitVersion + " (" + appVersionCode + ")")
} catch (ignored) {
println("Git not found, using " + gitVersion + " (" + appVersionCode + ")")
}
project.version = gitVersion
}
configurations {
customImplementation.extendsFrom implementation
}
task linphoneSdkSource() {
doLast {
configurations.customImplementation.getIncoming().each {
it.getResolutionResult().allComponents.each {
if (it.id.getDisplayName().contains("linphone-sdk-android")) {
println 'Linphone SDK used is ' + it.moduleVersion.version + ' from ' + it.properties["repositoryName"]
}
}
}
}
}
project.tasks['preBuild'].dependsOn 'getGitVersion'
project.tasks['preBuild'].dependsOn 'linphoneSdkSource'
android {
compileSdkVersion 33
buildToolsVersion '33.0.0'
defaultConfig {
minSdkVersion 23
targetSdkVersion 33
versionCode appVersionCode
versionName "${project.version}"
applicationId getPackageName()
}
applicationVariants.all { variant ->
variant.outputs.all {
outputFileName = "linphone-android-${variant.buildType.name}-${project.version}.apk"
}
var enableFirebaseService = "false"
if (firebaseEnabled) {
enableFirebaseService = "true"
}
// See https://developer.android.com/studio/releases/gradle-plugin#3-6-0-behavior for why extractNativeLibs is set to true in debug flavor
if (variant.buildType.name == "release" || variant.buildType.name == "releaseWithCrashlytics") {
variant.getMergedFlavor().manifestPlaceholders = [linphone_address_mime_type: "vnd.android.cursor.item/vnd." + getPackageName() + ".provider.sip_address",
linphone_file_provider: getPackageName() + ".fileprovider",
appLabel: "@string/app_name",
firebaseServiceEnabled: enableFirebaseService,
extractNativeLibs: "false"]
} else {
variant.getMergedFlavor().manifestPlaceholders = [linphone_address_mime_type: "vnd.android.cursor.item/vnd." + getPackageName() + ".provider.sip_address",
linphone_file_provider: getPackageName() + ".debug.fileprovider",
appLabel: "@string/app_name_debug",
firebaseServiceEnabled: enableFirebaseService,
extractNativeLibs: "true"]
}
}
def keystorePropertiesFile = rootProject.file("keystore.properties")
def keystoreProperties = new Properties()
keystoreProperties.load(new FileInputStream(keystorePropertiesFile))
signingConfigs {
release {
storeFile file(keystoreProperties['storeFile'])
storePassword keystoreProperties['storePassword']
keyAlias keystoreProperties['keyAlias']
keyPassword keystoreProperties['keyPassword']
}
}
buildTypes {
release {
minifyEnabled true
signingConfig signingConfigs.release
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
resValue "string", "linphone_app_branch", gitBranch.toString().trim()
resValue "string", "sync_account_type", getPackageName() + ".sync"
resValue "string", "file_provider", getPackageName() + ".fileprovider"
resValue "string", "linphone_address_mime_type", "vnd.android.cursor.item/vnd." + getPackageName() + ".provider.sip_address"
if (!firebaseEnabled) {
resValue "string", "gcm_defaultSenderId", "none"
}
resValue "bool", "crashlytics_enabled", "false"
}
releaseWithCrashlytics {
initWith release
resValue "bool", "crashlytics_enabled", crashlyticsEnabled.toString()
if (crashlyticsEnabled) {
firebaseCrashlytics {
nativeSymbolUploadEnabled true
unstrippedNativeLibsDir file(LinphoneSdkBuildDir + '/libs-debug/').toString()
}
}
}
debug {
applicationIdSuffix ".debug"
debuggable true
jniDebuggable true
resValue "string", "linphone_app_branch", gitBranch.toString().trim()
resValue "string", "sync_account_type", getPackageName() + ".sync"
resValue "string", "file_provider", getPackageName() + ".debug.fileprovider"
resValue "string", "linphone_address_mime_type", "vnd.android.cursor.item/vnd." + getPackageName() + ".provider.sip_address"
resValue "bool", "crashlytics_enabled", crashlyticsEnabled.toString()
if (!firebaseEnabled) {
resValue "string", "gcm_defaultSenderId", "none"
}
if (crashlyticsEnabled) {
firebaseCrashlytics {
nativeSymbolUploadEnabled true
unstrippedNativeLibsDir file(LinphoneSdkBuildDir + '/libs-debug/').toString()
}
}
}
}
buildFeatures {
dataBinding = true
}
namespace 'org.linphone'
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'androidx.appcompat:appcompat:1.6.0-rc01'
implementation 'androidx.core:core-ktx:1.9.0'
implementation 'androidx.core:core-splashscreen:1.0.0'
implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.5.1'
implementation 'androidx.media:media:1.6.0'
implementation "androidx.security:security-crypto-ktx:1.1.0-alpha03"
implementation "androidx.window:window:1.0.0"
def nav_version = "2.5.2"
implementation "androidx.navigation:navigation-fragment-ktx:$nav_version"
implementation "androidx.navigation:navigation-ui-ktx:$nav_version"
implementation "androidx.slidingpanelayout:slidingpanelayout:1.2.0"
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
implementation "androidx.gridlayout:gridlayout:1.0.0"
implementation 'androidx.recyclerview:recyclerview:1.3.0-rc01'
// https://github.com/material-components/material-components-android/blob/master/LICENSE Apache v2.0
implementation 'com.google.android.material:material:1.6.1'
// https://github.com/google/flexbox-layout/blob/main/LICENSE Apache v2.0
implementation 'com.google.android.flexbox:flexbox:3.0.0'
implementation 'androidx.emoji:emoji:1.1.0'
implementation 'androidx.emoji:emoji-bundled:1.1.0'
// https://github.com/coil-kt/coil/blob/main/LICENSE.txt Apache v2.0
def coil_version = "2.1.0"
implementation("io.coil-kt:coil:$coil_version")
implementation("io.coil-kt:coil-gif:$coil_version")
implementation("io.coil-kt:coil-svg:$coil_version")
implementation("io.coil-kt:coil-video:$coil_version")
// https://github.com/Baseflow/PhotoView/blob/master/LICENSE Apache v2.0
implementation 'com.github.chrisbanes:PhotoView:2.3.0'
implementation platform('com.google.firebase:firebase-bom:30.3.2')
if (crashlyticsEnabled) {
implementation 'com.google.firebase:firebase-crashlytics-ndk'
} else {
compileOnly 'com.google.firebase:firebase-crashlytics-ndk'
}
if (firebaseEnabled) {
implementation 'com.google.firebase:firebase-messaging'
}
implementation 'org.linphone:linphone-sdk-android:5.2+'
// Only enable leak canary prior to release
//debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.4'
}
task generateContactsXml(type: Copy) {
from 'contacts.xml'
into "src/main/res/xml/"
outputs.upToDateWhen { file('src/main/res/xml/contacts.xml').exists() }
filter {
line -> line
.replaceAll('%%AUTO_GENERATED%%', 'This file has been automatically generated, do not edit or commit !')
.replaceAll('%%PACKAGE_NAME%%', getPackageName())
}
}
project.tasks['preBuild'].dependsOn 'generateContactsXml'
ktlint {
android = true
ignoreFailures = true
}
project.tasks['preBuild'].dependsOn 'ktlintFormat'
if (crashlyticsEnabled) {
afterEvaluate {
assembleDebug.finalizedBy(uploadCrashlyticsSymbolFileDebug)
packageDebugBundle.finalizedBy(uploadCrashlyticsSymbolFileDebug)
assembleReleaseWithCrashlytics.finalizedBy(uploadCrashlyticsSymbolFileReleaseWithCrashlytics)
packageReleaseWithCrashlytics.finalizedBy(uploadCrashlyticsSymbolFileReleaseWithCrashlytics)
}
}

319
app/build.gradle.kts Normal file
View file

@ -0,0 +1,319 @@
import com.android.build.gradle.internal.tasks.factory.dependsOn
import com.google.firebase.crashlytics.buildtools.gradle.CrashlyticsExtension
import com.google.gms.googleservices.GoogleServicesPlugin
import java.io.BufferedReader
import java.io.FileInputStream
import java.util.Properties
plugins {
alias(libs.plugins.androidApplication)
alias(libs.plugins.kapt)
alias(libs.plugins.ktlint)
alias(libs.plugins.jetbrainsKotlinAndroid)
alias(libs.plugins.navigation)
alias(libs.plugins.crashlytics)
}
val packageName = "org.linphone"
val useDifferentPackageNameForDebugBuild = false
val sdkPath = providers.gradleProperty("LinphoneSdkBuildDir").get()
val googleServices = File(projectDir.absolutePath + "/google-services.json")
val linphoneLibs = File("$sdkPath/libs/")
val linphoneDebugLibs = File("$sdkPath/libs-debug/")
val firebaseCloudMessagingAvailable = googleServices.exists()
val crashlyticsAvailable = googleServices.exists() && linphoneLibs.exists() && linphoneDebugLibs.exists()
if (firebaseCloudMessagingAvailable) {
println("google-services.json found, enabling CloudMessaging feature")
apply<GoogleServicesPlugin>()
} else {
println("google-services.json not found, disabling CloudMessaging feature")
}
var gitVersion = "6.1.0-alpha"
var gitBranch = ""
try {
val gitDescribe = ProcessBuilder()
.command("git", "describe", "--abbrev=0")
.directory(project.rootDir)
.start()
.inputStream.bufferedReader().use(BufferedReader::readText)
.trim()
println("Git describe: $gitDescribe")
val gitCommitsCount = ProcessBuilder()
.command("git", "rev-list", "$gitDescribe..HEAD", "--count")
.directory(project.rootDir)
.start()
.inputStream.bufferedReader().use(BufferedReader::readText)
.trim()
println("Git commits count: $gitCommitsCount")
val gitCommitHash = ProcessBuilder()
.command("git", "rev-parse", "--short", "HEAD")
.directory(project.rootDir)
.start()
.inputStream.bufferedReader().use(BufferedReader::readText)
.trim()
println("Git commit hash: $gitCommitHash")
gitBranch = ProcessBuilder()
.command("git", "name-rev", "--name-only", "HEAD")
.directory(project.rootDir)
.start()
.inputStream.bufferedReader().use(BufferedReader::readText)
.trim()
println("Git branch name: $gitBranch")
gitVersion =
if (gitCommitsCount.toInt() == 0) {
gitDescribe
} else {
"$gitDescribe.$gitCommitsCount+$gitCommitHash"
}
} catch (e: Exception) {
println("Git not found [$e], using $gitVersion")
}
println("Computed git version: $gitVersion")
configurations {
implementation { isCanBeResolved = true }
}
tasks.register("linphoneSdkSource") {
doLast {
configurations.implementation.get().incoming.resolutionResult.allComponents.forEach {
if (it.id.displayName.contains("linphone-sdk-android")) {
println("Linphone SDK used is ${it.moduleVersion?.version}")
}
}
}
}
project.tasks.preBuild.dependsOn("linphoneSdkSource")
android {
namespace = "org.linphone"
compileSdk = 36
defaultConfig {
applicationId = packageName
minSdk = 28
targetSdk = 36
versionCode = 601002 // 6.01.002
versionName = "6.1.0-alpha"
manifestPlaceholders["appAuthRedirectScheme"] = packageName
ndk {
//noinspection ChromeOsAbiSupport
abiFilters += listOf("armeabi-v7a", "arm64-v8a")
}
}
applicationVariants.all {
val variant = this
variant.outputs
.map { it as com.android.build.gradle.internal.api.BaseVariantOutputImpl }
.forEach { output ->
output.outputFileName = "linphone-android-${variant.buildType.name}-${project.version}.apk"
}
}
val keystorePropertiesFile = rootProject.file("keystore.properties")
val keystoreProperties = Properties()
keystoreProperties.load(FileInputStream(keystorePropertiesFile))
signingConfigs {
create("release") {
val keyStorePath = keystoreProperties["storeFile"] as String
val keyStore = project.file(keyStorePath)
if (keyStore.exists()) {
storeFile = keyStore
storePassword = keystoreProperties["storePassword"] as String
keyAlias = keystoreProperties["keyAlias"] as String
keyPassword = keystoreProperties["keyPassword"] as String
println("Signing config release is using keystore [$storeFile]")
} else {
println("Keystore [$storeFile] doesn't exists!")
}
}
}
buildTypes {
getByName("debug") {
if (useDifferentPackageNameForDebugBuild) {
applicationIdSuffix = ".debug"
}
isDebuggable = true
isJniDebuggable = true
val appVersion = gitVersion
val appBranch = gitBranch
println("Setting app version [$appVersion] app branch [$appBranch]")
resValue("string", "linphone_app_version", appVersion)
resValue("string", "linphone_app_branch", appBranch)
if (useDifferentPackageNameForDebugBuild) {
resValue("string", "file_provider", "$packageName.debug.fileprovider")
} else {
resValue("string", "file_provider", "$packageName.fileprovider")
}
resValue("string", "linphone_openid_callback_scheme", packageName)
if (crashlyticsAvailable) {
val path = File("$sdkPath/libs-debug/").toString()
configure<CrashlyticsExtension> {
nativeSymbolUploadEnabled = true
unstrippedNativeLibsDir = path
}
}
buildConfigField("Boolean", "CRASHLYTICS_ENABLED", crashlyticsAvailable.toString())
}
getByName("release") {
isMinifyEnabled = true
isShrinkResources = true
proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro",
)
signingConfig = signingConfigs.getByName("release")
val appVersion = gitVersion
val appBranch = gitBranch
println("Setting app version [$appVersion] app branch [$appBranch]")
resValue("string", "linphone_app_version", appVersion)
resValue("string", "linphone_app_branch", appBranch)
resValue("string", "file_provider", "$packageName.fileprovider")
resValue("string", "linphone_openid_callback_scheme", packageName)
if (crashlyticsAvailable) {
val path = File("$sdkPath/libs-debug/").toString()
configure<CrashlyticsExtension> {
nativeSymbolUploadEnabled = true
unstrippedNativeLibsDir = path
}
}
buildConfigField("Boolean", "CRASHLYTICS_ENABLED", crashlyticsAvailable.toString())
}
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
}
buildFeatures {
dataBinding = true
buildConfig = true
}
lint {
abortOnError = false
}
}
dependencies {
implementation(libs.androidx.annotations)
implementation(libs.androidx.appcompat)
implementation(libs.androidx.constraint.layout)
implementation(libs.androidx.core.ktx)
implementation(libs.androidx.splashscreen)
implementation(libs.androidx.telecom)
implementation(libs.androidx.media)
implementation(libs.androidx.recyclerview)
implementation(libs.androidx.swiperefreshlayout)
implementation(libs.androidx.slidingpanelayout)
implementation(libs.androidx.window)
implementation(libs.androidx.gridlayout)
implementation(libs.androidx.security.crypto.ktx)
implementation(libs.androidx.navigation.fragment.ktx)
implementation(libs.androidx.navigation.ui.ktx)
implementation(libs.androidx.emoji2)
implementation(libs.androidx.car)
// https://github.com/google/flexbox-layout/blob/main/LICENSE Apache v2.0
implementation(libs.google.flexbox)
// https://github.com/material-components/material-components-android/blob/master/LICENSE Apache v2.0
implementation(libs.google.material)
// To be able to parse native crash tombstone and print them with SDK logs the next time the app will start
implementation(libs.google.protobuf)
implementation(platform(libs.google.firebase.bom))
implementation(libs.google.firebase.messaging)
implementation(libs.google.firebase.crashlytics)
// https://github.com/coil-kt/coil/blob/main/LICENSE.txt Apache v2.0
implementation(libs.coil)
implementation(libs.coil.gif)
implementation(libs.coil.svg)
implementation(libs.coil.video)
// https://github.com/tommybuonomo/dotsindicator/blob/master/LICENSE Apache v2.0
implementation(libs.dots.indicator)
// https://github.com/Baseflow/PhotoView/blob/master/LICENSE Apache v2.0
implementation(libs.photoview)
// https://github.com/openid/AppAuth-Android/blob/master/LICENSE Apache v2.0
implementation(libs.openid.appauth)
implementation(libs.linphone)
}
configure<org.jlleitschuh.gradle.ktlint.KtlintExtension> {
android.set(true)
ignoreFailures.set(true)
additionalEditorconfig.set(
mapOf(
"max_line_length" to "120",
"ktlint_standard_max-line-length" to "disabled",
"ktlint_standard_function-signature" to "disabled",
"ktlint_standard_no-blank-line-before-rbrace" to "disabled",
"ktlint_standard_no-empty-class-body" to "disabled",
"ktlint_standard_annotation-spacing" to "disabled",
"ktlint_standard_class-signature" to "disabled",
"ktlint_standard_function-expression-body" to "disabled",
"ktlint_standard_function-type-modifier-spacing" to "disabled",
"ktlint_standard_if-else-wrapping" to "disabled",
"ktlint_standard_argument-list-wrapping" to "disabled",
"ktlint_standard_trailing-comma-on-call-site" to "disabled",
"ktlint_standard_trailing-comma-on-declaration-site" to "disabled",
"ktlint_standard_no-empty-first-line-in-class-body" to "disabled",
"ktlint_standard_no-empty-first-line-in-method-block" to "disabled",
"ktlint_standard_no-trailing-spaces" to "disabled",
"ktlint_standard_no-blank-line-in-list" to "disabled",
"ktlint_standard_no-multi-spaces" to "disabled",
"ktlint_standard_try-catch-finally-spacing" to "disabled",
"ktlint_standard_block-comment-initial-star-alignment" to "disabled",
"ktlint_standard_spacing-between-declarations-with-comments" to "disabled",
"ktlint_standard_no-consecutive-comments" to "disabled",
"ktlint_standard_multiline-expression-wrapping" to "disabled",
"ktlint_standard_parameter-list-wrapping" to "disabled",
"ktlint_standard_comment-wrapping" to "disabled",
"ktlint_standard_discouraged-comment-location" to "disabled",
"ktlint_standard_string-template-indent" to "disabled",
"ktlint_standard_parameter-list-spacing" to "disabled",
"ktlint_standard_statement-wrapping" to "disabled",
"ktlint_standard_import-ordering" to "disabled",
"ktlint_standard_paren-spacing" to "disabled",
"ktlint_standard_curly-spacing" to "disabled",
"ktlint_standard_indent" to "disabled",
)
)
}
project.tasks.preBuild.dependsOn("ktlintFormat")
if (crashlyticsAvailable) {
afterEvaluate {
tasks.getByName("assembleDebug").finalizedBy(
tasks.getByName("uploadCrashlyticsSymbolFileDebug"),
)
tasks.getByName("packageDebug").finalizedBy(
tasks.getByName("uploadCrashlyticsSymbolFileDebug"),
)
tasks.getByName("assembleRelease").finalizedBy(
tasks.getByName("uploadCrashlyticsSymbolFileRelease"),
)
tasks.getByName("packageRelease").finalizedBy(
tasks.getByName("uploadCrashlyticsSymbolFileRelease"),
)
}
}

View file

@ -1,11 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<ContactsSource xmlns:android="http://schemas.android.com/apk/res/android">
<!-- %%AUTO_GENERATED%% -->
<ContactsDataKind
android:detailColumn="data3"
android:detailSocialSummary="true"
android:icon="@drawable/linphone_logo_tinted"
android:mimeType="vnd.android.cursor.item/vnd.%%PACKAGE_NAME%%.provider.sip_address"
android:summaryColumn="data2" />
<!-- You can't use @string/linphone_address_mime_type above ! You have to hardcode it... -->
</ContactsSource>

View file

@ -1,6 +1,6 @@
# Add project specific ProGuard rules here. # Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the # You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle. # proguardFiles setting in build.gradle.kts.
# #
# For more details, see # For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html # http://developer.android.com/guide/developing/tools/proguard.html
@ -19,6 +19,3 @@
# If you keep the line number information, uncomment this to # If you keep the line number information, uncomment this to
# hide the original source file name. # hide the original source file name.
#-renamesourcefileattribute SourceFile #-renamesourcefileattribute SourceFile
-keep public class * extends androidx.fragment.app.Fragment { *; }
-dontwarn com.google.errorprone.annotations.Immutable

View file

@ -1,84 +1,87 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"> <manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<uses-feature
android:name="android.hardware.camera"
android:required="false" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<!-- To be able to display contacts list & match calling/called numbers --> <!-- To be able to display contacts list & match calling/called numbers -->
<uses-permission android:name="android.permission.READ_CONTACTS" /> <uses-permission android:name="android.permission.READ_CONTACTS" />
<!-- For in-app contact edition -->
<uses-permission android:name="android.permission.WRITE_CONTACTS" />
<!-- Helps filling phone number and country code in assistant -->
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<!-- Needed for auto start at boot and to ensure the service won't be killed by OS while in call -->
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<!-- Starting Android 13 we need to ask notification permission --> <!-- Starting Android 13 we need to ask notification permission -->
<uses-permission android:name="android.permission.POST_NOTIFICATIONS"/> <uses-permission android:name="android.permission.POST_NOTIFICATIONS"/>
<!-- Needed for full screen intent in incoming call notifications --> <!-- Needed for full screen intent in incoming call notifications -->
<uses-permission android:name="android.permission.USE_FULL_SCREEN_INTENT" /> <uses-permission android:name="android.permission.USE_FULL_SCREEN_INTENT" />
<!-- To vibrate when pressing DTMF keys on numpad & incoming calls --> <!-- To vibrate while receiving an incoming call -->
<uses-permission android:name="android.permission.VIBRATE" /> <uses-permission android:name="android.permission.VIBRATE" />
<!-- Needed to attach file(s) in chat room fragment --> <!-- Needed for foreground service
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" (https://developer.android.com/guide/components/foreground-services) -->
android:maxSdkVersion="32"/>
<!-- Starting Android 13 you need those 3 permissions instead (https://developer.android.com/about/versions/13/behavior-changes-13) -->
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
<uses-permission android:name="android.permission.READ_MEDIA_VIDEO" />
<uses-permission android:name="android.permission.READ_MEDIA_AUDIO" />
<!-- Needed to shared downloaded files if setting is on -->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"
android:maxSdkVersion="28" />
<!-- Both permissions below are for contacts sync account, needed to store presence in native contact if enabled -->
<uses-permission android:name="android.permission.AUTHENTICATE_ACCOUNTS" />
<uses-permission android:name="android.permission.GET_ACCOUNTS" />
<!-- Needed for Telecom Manager -->
<uses-permission android:name="android.permission.MANAGE_OWN_CALLS"/> <uses-permission android:name="android.permission.MANAGE_OWN_CALLS"/>
<uses-permission android:name="android.permission.READ_PHONE_NUMBERS" /> <uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
<!-- Needed for Android 14
https://developer.android.com/about/versions/14/behavior-changes-14#fgs-types -->
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_CAMERA" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_MICROPHONE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_PHONE_CALL" />
<!-- Required for foreground service started when a push is being received,
without it app won't be able to access network if data saver is ON (for example) -->
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_DATA_SYNC" />
<!-- Needed to keep a permanent foreground service and keep app alive to be able to receive
messages & calls for third party accounts for which push notifications aren't available,
and starting Android 15 dataSync is limited to 6 hours per day
and can't be used with RECEIVE_BOOT_COMPLETED intent either -->
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_SPECIAL_USE" />
<!-- Needed for overlay --> <!-- Needed for auto start at boot if keep alive service is enabled -->
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" /> <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<!-- Needed to check current Do not disturb policy -->
<uses-permission android:name="android.permission.ACCESS_NOTIFICATION_POLICY" />
<!-- Needed starting Android 12 for broadcast receiver
to be triggered when BT device is connected / disconnected
(https://developer.android.com/guide/topics/connectivity/bluetooth/permissions) -->
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
<application <application
android:name=".LinphoneApplication" android:name=".LinphoneApplication"
android:allowBackup="false" android:allowBackup="true"
android:enableOnBackInvokedCallback="true" android:dataExtractionRules="@xml/data_extraction_rules"
android:fullBackupContent="@xml/backup_rules"
android:icon="@mipmap/ic_launcher" android:icon="@mipmap/ic_launcher"
android:label="${appLabel}"
android:localeConfig="@xml/locales_config"
android:roundIcon="@mipmap/ic_launcher_round" android:roundIcon="@mipmap/ic_launcher_round"
android:extractNativeLibs="${extractNativeLibs}" android:label="@string/app_name"
android:theme="@style/AppTheme" android:enableOnBackInvokedCallback="true"
android:allowNativeHeapPointerTagging="false"> android:localeConfig="@xml/locales_config"
android:theme="@style/Theme.Linphone"
android:appCategory="social"
android:largeHeap="true"
tools:targetApi="35">
<activity android:name=".activities.main.MainActivity" <!-- Required for chat message & call notifications to be displayed in Android auto -->
android:exported="true" <meta-data
android:launchMode="singleTask" android:name="com.google.android.gms.car.application"
android:resource="@xml/automotive_app_desc"/>
<!--<meta-data
android:name="androidx.car.app.minCarApiLevel"
android:value="1"/>-->
<activity
android:name=".ui.main.MainActivity"
android:theme="@style/AppSplashScreenTheme"
android:windowSoftInputMode="adjustResize" android:windowSoftInputMode="adjustResize"
android:theme="@style/AppSplashScreenTheme"> android:exported="true"
<nav-graph android:value="@navigation/main_nav_graph" /> android:launchMode="singleTask">
<meta-data
android:name="android.app.shortcuts"
android:resource="@xml/shortcuts" />
<intent-filter> <intent-filter>
<action android:name="android.intent.action.MAIN" /> <action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" /> <category android:name="android.intent.category.LAUNCHER" />
</intent-filter> </intent-filter>
<meta-data
android:name="android.app.shortcuts"
android:resource="@xml/shortcuts" />
<intent-filter> <intent-filter>
<action android:name="android.intent.action.VIEW_LOCUS" /> <action android:name="android.intent.action.VIEW_LOCUS" />
</intent-filter> </intent-filter>
@ -103,63 +106,83 @@
</intent-filter> </intent-filter>
<intent-filter> <intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.BROWSABLE" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="${linphone_address_mime_type}" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<action android:name="android.intent.action.DIAL" /> <action android:name="android.intent.action.DIAL" />
<action android:name="android.intent.action.VIEW" />
<action android:name="android.intent.action.CALL" /> <action android:name="android.intent.action.CALL" />
<category android:name="android.intent.category.DEFAULT" /> <category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" /> <category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="tel" /> <data android:scheme="tel" />
<data android:scheme="sip" /> <data android:scheme="sip" />
<data android:scheme="callto" />
<data android:scheme="sips" /> <data android:scheme="sips" />
<data android:scheme="linphone" />
<data android:scheme="sip-linphone" /> <data android:scheme="sip-linphone" />
<data android:scheme="sips-linphone" />
<data android:scheme="linphone-sip" />
<data android:scheme="linphone-sips" />
<data android:scheme="linphone-config" />
</intent-filter> </intent-filter>
<intent-filter>
<action android:name="android.intent.action.SEND" />
<action android:name="android.intent.action.SENDTO" />
<category android:name="android.intent.category.DEFAULT" />
<data android:scheme="sms" />
<data android:scheme="smsto" />
<data android:scheme="mms" />
<data android:scheme="mmsto" />
</intent-filter>
</activity> </activity>
<activity android:name=".activities.assistant.AssistantActivity" <activity
android:windowSoftInputMode="adjustResize"/> android:name=".ui.welcome.WelcomeActivity"
android:windowSoftInputMode="adjustResize"
<activity android:name=".activities.voip.CallActivity"
android:launchMode="singleTask" android:launchMode="singleTask"
android:resizeableActivity="true" />
<activity
android:name=".ui.assistant.AssistantActivity"
android:windowSoftInputMode="adjustResize"
android:launchMode="singleTask"
android:resizeableActivity="true" />
<activity
android:name=".ui.fileviewer.MediaViewerActivity"
android:windowSoftInputMode="adjustResize"
android:launchMode="singleTask"
android:resizeableActivity="true" />
<activity
android:name=".ui.fileviewer.FileViewerActivity"
android:windowSoftInputMode="adjustResize"
android:launchMode="singleTask"
android:resizeableActivity="true" />
<activity
android:name=".ui.call.CallActivity"
android:windowSoftInputMode="adjustResize"
android:theme="@style/Theme.LinphoneInCall"
android:launchMode="singleInstance"
android:turnScreenOn="true" android:turnScreenOn="true"
android:showWhenLocked="true" android:showWhenLocked="true"
android:resizeableActivity="true" android:resizeableActivity="true"
android:supportsPictureInPicture="true" /> android:supportsPictureInPicture="true" />
<activity
android:name=".activities.chat_bubble.ChatBubbleActivity"
android:allowEmbedded="true"
android:documentLaunchMode="always"
android:resizeableActivity="true" />
<!-- Services --> <!-- Services -->
<service <service
android:name=".core.CoreService" android:name=".core.CoreInCallService"
android:exported="false" android:exported="false"
android:foregroundServiceType="phoneCall|camera|microphone" android:foregroundServiceType="phoneCall|camera|microphone"
android:stopWithTask="false" android:stopWithTask="false"
android:label="@string/app_name" /> android:label="@string/app_name" />
<service
android:name=".core.CorePushService"
android:exported="false"
android:foregroundServiceType="dataSync"
android:stopWithTask="false"
android:label="@string/app_name" />
<service
android:name=".core.CoreFileTransferService"
android:exported="false"
android:foregroundServiceType="dataSync"
android:stopWithTask="false"
android:label="@string/app_name" />
<service android:name="org.linphone.core.tools.firebase.FirebaseMessaging" <service android:name="org.linphone.core.tools.firebase.FirebaseMessaging"
android:enabled="${firebaseServiceEnabled}" android:enabled="true"
android:exported="false"> android:exported="false">
<intent-filter> <intent-filter>
<action android:name="com.google.firebase.MESSAGING_EVENT" /> <action android:name="com.google.firebase.MESSAGING_EVENT" />
@ -167,38 +190,24 @@
</service> </service>
<service <service
android:name=".contact.DummySyncService" android:name=".core.CoreKeepAliveThirdPartyAccountsService"
android:exported="true"> android:exported="false"
<intent-filter> android:foregroundServiceType="specialUse"
<action android:name="android.content.SyncAdapter" /> android:stopWithTask="false"
</intent-filter> android:label="@string/app_name">
<property
<meta-data android:name="android.app.PROPERTY_SPECIAL_USE_FGS_SUBTYPE"
android:name="android.content.SyncAdapter" android:value="Needed to keep app alive to be able to receive messages and calls from third party SIP servers for which push notifications aren't available." />
android:resource="@xml/sync_adapter" />
<meta-data
android:name="android.provider.CONTACTS_STRUCTURE"
android:resource="@xml/contacts" />
</service> </service>
<service android:name=".contact.DummyAuthenticationService" <!--<service
android:name=".telecom.auto.AndroidAutoService"
android:exported="true"> android:exported="true">
<intent-filter> <intent-filter>
<action android:name="android.accounts.AccountAuthenticator" /> <action android:name="androidx.car.app.CarAppService"/>
<category android:name="androidx.car.app.category.CALLING"/>
</intent-filter> </intent-filter>
</service>-->
<meta-data
android:name="android.accounts.AccountAuthenticator"
android:resource="@xml/authenticator" />
</service>
<service android:name=".telecom.TelecomConnectionService"
android:permission="android.permission.BIND_TELECOM_CONNECTION_SERVICE"
android:exported="true">
<intent-filter>
<action android:name="android.telecom.ConnectionService" />
</intent-filter>
</service>
<!-- Receivers --> <!-- Receivers -->
@ -226,7 +235,7 @@
<provider <provider
android:name="androidx.core.content.FileProvider" android:name="androidx.core.content.FileProvider"
android:authorities="${linphone_file_provider}" android:authorities="@string/file_provider"
android:exported="false" android:exported="false"
android:grantUriPermissions="true"> android:grantUriPermissions="true">
<meta-data <meta-data

View file

@ -3,7 +3,8 @@
<section name="proxy_default_values"> <section name="proxy_default_values">
<entry name="avpf" overwrite="true">1</entry> <entry name="avpf" overwrite="true">1</entry>
<entry name="dial_escape_plus" overwrite="true">0</entry> <entry name="dial_escape_plus" overwrite="true">0</entry>
<entry name="publish" overwrite="true">0</entry> <entry name="publish" overwrite="true">1</entry>
<entry name="publish_expires" overwrite="true">120</entry>
<entry name="quality_reporting_collector" overwrite="true">sip:voip-metrics@sip.linphone.org;transport=tls</entry> <entry name="quality_reporting_collector" overwrite="true">sip:voip-metrics@sip.linphone.org;transport=tls</entry>
<entry name="quality_reporting_enabled" overwrite="true">1</entry> <entry name="quality_reporting_enabled" overwrite="true">1</entry>
<entry name="quality_reporting_interval" overwrite="true">180</entry> <entry name="quality_reporting_interval" overwrite="true">180</entry>
@ -20,22 +21,15 @@
<entry name="cpim_in_basic_chat_rooms_enabled" overwrite="true">1</entry> <entry name="cpim_in_basic_chat_rooms_enabled" overwrite="true">1</entry>
<entry name="rtp_bundle" overwrite="true">1</entry> <entry name="rtp_bundle" overwrite="true">1</entry>
<entry name="lime_server_url" overwrite="true">https://lime.linphone.org/lime-server/lime-server.php</entry> <entry name="lime_server_url" overwrite="true">https://lime.linphone.org/lime-server/lime-server.php</entry>
<entry name="lime_algo" overwrite="true">c25519</entry>
<entry name="supported" overwrite="true"></entry>
</section> </section>
<section name="nat_policy_default_values"> <section name="nat_policy_default_values">
<entry name="stun_server" overwrite="true">stun.linphone.org</entry> <entry name="stun_server" overwrite="true">stun.linphone.org</entry>
<entry name="protocols" overwrite="true">stun,ice</entry> <entry name="protocols" overwrite="true">stun,ice</entry>
</section> </section>
<section name="net"> <section name="sip">
<entry name="friendlist_subscription_enabled" overwrite="true">1</entry> <entry name="media_encryption" overwrite="true">srtp</entry>
</section> <entry name="media_encryption_mandatory">1</entry>
<section name="assistant">
<entry name="domain" overwrite="true">sip.linphone.org</entry>
<entry name="algorithm" overwrite="true">SHA-256</entry>
<entry name="password_max_length" overwrite="true">-1</entry>
<entry name="password_min_length" overwrite="true">1</entry>
<entry name="username_length" overwrite="true">-1</entry>
<entry name="username_max_length" overwrite="true">64</entry>
<entry name="username_min_length" overwrite="true">1</entry>
<entry name="username_regex" overwrite="true">^[a-z0-9+_.\-]*$</entry>
</section> </section>
</config> </config>

View file

@ -4,6 +4,7 @@
<entry name="avpf" overwrite="true">0</entry> <entry name="avpf" overwrite="true">0</entry>
<entry name="dial_escape_plus" overwrite="true">0</entry> <entry name="dial_escape_plus" overwrite="true">0</entry>
<entry name="publish" overwrite="true">0</entry> <entry name="publish" overwrite="true">0</entry>
<entry name="publish_expires" overwrite="true">-1</entry>
<entry name="quality_reporting_collector" overwrite="true"></entry> <entry name="quality_reporting_collector" overwrite="true"></entry>
<entry name="quality_reporting_enabled" overwrite="true">0</entry> <entry name="quality_reporting_enabled" overwrite="true">0</entry>
<entry name="quality_reporting_interval" overwrite="true">0</entry> <entry name="quality_reporting_interval" overwrite="true">0</entry>
@ -20,22 +21,18 @@
<entry name="cpim_in_basic_chat_rooms_enabled" overwrite="true">0</entry> <entry name="cpim_in_basic_chat_rooms_enabled" overwrite="true">0</entry>
<entry name="rtp_bundle" overwrite="true">0</entry> <entry name="rtp_bundle" overwrite="true">0</entry>
<entry name="lime_server_url" overwrite="true"></entry> <entry name="lime_server_url" overwrite="true"></entry>
<entry name="lime_algo" overwrite="true"></entry>
<entry name="supported" overwrite="true">outbound</entry>
</section> </section>
<section name="nat_policy_default_values"> <section name="nat_policy_default_values">
<entry name="stun_server" overwrite="true"></entry> <entry name="stun_server" overwrite="true">stun.linphone.org</entry>
<entry name="protocols" overwrite="true"></entry> <entry name="protocols" overwrite="true">stun,ice</entry>
</section> </section>
<section name="net"> <section name="sip">
<entry name="friendlist_subscription_enabled" overwrite="true">0</entry> <entry name="media_encryption">srtp</entry>
<entry name="media_encryption_mandatory" overwrite="true">0</entry>
</section> </section>
<section name="assistant"> <section name="ui">
<entry name="domain" overwrite="true"></entry> <entry name="automatically_show_dialpad" overwrite="true">1</entry>
<entry name="algorithm" overwrite="true">MD5</entry>
<entry name="password_max_length" overwrite="true">-1</entry>
<entry name="password_min_length" overwrite="true">0</entry>
<entry name="username_length" overwrite="true">-1</entry>
<entry name="username_max_length" overwrite="true">128</entry>
<entry name="username_min_length" overwrite="true">1</entry>
<entry name="username_regex" overwrite="true">^[a-zA-Z0-9+_.\-]*$</entry>
</section> </section>
</config> </config>

View file

@ -10,6 +10,10 @@ sip_port=-1
sip_tcp_port=-1 sip_tcp_port=-1
sip_tls_port=-1 sip_tls_port=-1
media_encryption=none media_encryption=none
update_presence_model_timestamp_before_publish_expires_refresh=1
use_rfc2833=1
use_info=1
rls_uri=sips:rls@sip.linphone.org
[net] [net]
#Because dynamic bitrate adaption can increase bitrate, we must allow "no limit" #Because dynamic bitrate adaption can increase bitrate, we must allow "no limit"
@ -18,26 +22,38 @@ upload_bw=0
[video] [video]
size=vga size=vga
automatically_accept=1
automatically_initiate=0
automatically_accept_direction=2 #receive only
[app] [app]
tunnel=disabled tunnel=disabled
auto_start=1 auto_download_incoming_voice_recordings=1
record_aware=1 auto_download_incoming_icalendars=1
[tunnel] [tunnel]
host= host=
port=443 port=443
[misc] [misc]
log_collection_upload_server_url=https://www.linphone.org:444/lft.php log_collection_upload_server_url=https://files.linphone.org/http-file-transfer-server/hft.php
file_transfer_server_url=https://www.linphone.org:444/lft.php file_transfer_server_url=https://files.linphone.org/http-file-transfer-server/hft.php
version_check_url_root=https://www.linphone.org/releases version_check_url_root=https://download.linphone.org/releases
max_calls=10 max_calls=10
history_max_size=100 history_max_size=100
conference_layout=1 conference_layout=1
hide_empty_chat_rooms=1
[in-app-purchase] [fec]
server_url=https://subscribe.linphone.org:444/inapp.php fec_enabled=1
purchasable_items_ids=test_account_subscription
[magic_search]
return_empty_friends=1
[chat]
imdn_to_everybody_threshold=1
[ui]
contacts_filter=sip.linphone.org
## End of default rc ## End of default rc

View file

@ -8,6 +8,9 @@
mtu=1300 mtu=1300
force_ice_disablement=0 force_ice_disablement=0
[rtp]
accept_any_encryption=1
[sip] [sip]
guess_hostname=1 guess_hostname=1
register_only_when_network_is_up=1 register_only_when_network_is_up=1
@ -15,37 +18,42 @@ auto_net_state_mon=1
auto_answer_replacing_calls=1 auto_answer_replacing_calls=1
ping_with_options=0 ping_with_options=0
use_cpim=1 use_cpim=1
zrtp_key_agreements_suites=MS_ZRTP_KEY_AGREEMENT_K255_KYB512 zrtp_key_agreements_suites=MS_ZRTP_KEY_AGREEMENT_K255_MLK512,MS_ZRTP_KEY_AGREEMENT_K255_KYB512
chat_messages_aggregation_delay=1000 chat_messages_aggregation_delay=1000
chat_messages_aggregation=1 chat_messages_aggregation=1
update_presence_model_timestamp_before_publish_expires_refresh=1
[sound] [sound]
#remove this property for any application that is not Linphone public version itself #remove this property for any application that is not Linphone public version itself
ec_calibrator_cool_tones=1 ec_calibrator_cool_tones=1
disable_ringing=0
[audio]
android_disable_audio_focus_requests=1
android_monitor_audio_devices=0
[video] [video]
displaytype=MSAndroidTextureDisplay displaytype=MSAndroidTextureDisplay
auto_resize_preview_to_keep_ratio=1 auto_resize_preview_to_keep_ratio=1
max_mosaic_size=vga max_conference_size=vga
[misc] [misc]
enable_basic_to_client_group_chat_room_migration=0 enable_basic_to_client_group_chat_room_migration=0
enable_simple_group_chat_message_state=0 enable_simple_group_chat_message_state=0
aggregate_imdn=1 aggregate_imdn=1
notify_each_friend_individually_when_presence_received=0 notify_each_friend_individually_when_presence_received=0
store_friends=0
[app] [app]
activation_code_length=4
prefer_basic_chat_room=1
record_aware=1 record_aware=1
[assistant]
xmlrpc_url=https://subscribe.linphone.org:444/wizard.php
[account_creator] [account_creator]
backend=0 url=https://subscribe.linphone.org/api/
[lime] [lime]
lime_update_threshold=-1 lime_update_threshold=86400
[alerts]
alerts_enabled=1
## End of factory rc ## End of factory rc

View file

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2010-2020 Belledonne Communications SARL. * Copyright (c) 2010-2023 Belledonne Communications SARL.
* *
* This file is part of linphone-android * This file is part of linphone-android
* (see https://www.linphone.org). * (see https://www.linphone.org).
@ -22,108 +22,142 @@ package org.linphone
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.app.Application import android.app.Application
import android.content.Context import android.content.Context
import coil.ImageLoader import android.os.PowerManager
import coil.ImageLoaderFactory import androidx.annotation.MainThread
import coil.decode.GifDecoder import coil3.ImageLoader
import coil.decode.ImageDecoderDecoder import coil3.SingletonImageLoader
import coil.decode.SvgDecoder import coil3.disk.DiskCache
import coil.decode.VideoFrameDecoder import coil3.disk.directory
import coil.disk.DiskCache import coil3.imageLoader
import coil.memory.MemoryCache import coil3.memory.MemoryCache
import org.linphone.core.* import coil3.request.CachePolicy
import coil3.request.crossfade
import coil3.svg.SvgDecoder
import coil3.video.VideoFrameDecoder
import com.google.android.material.color.DynamicColors
import org.linphone.compatibility.Compatibility
import org.linphone.core.CoreContext
import org.linphone.core.CorePreferences
import org.linphone.core.Factory
import org.linphone.core.LogCollectionState
import org.linphone.core.LogLevel
import org.linphone.core.VFS
import org.linphone.core.tools.Log import org.linphone.core.tools.Log
import org.linphone.mediastream.Version
class LinphoneApplication : Application(), ImageLoaderFactory { @MainThread
class LinphoneApplication : Application(), SingletonImageLoader.Factory {
companion object { companion object {
private const val TAG = "[Linphone Application]"
@SuppressLint("StaticFieldLeak") @SuppressLint("StaticFieldLeak")
lateinit var corePreferences: CorePreferences lateinit var corePreferences: CorePreferences
@SuppressLint("StaticFieldLeak") @SuppressLint("StaticFieldLeak")
lateinit var coreContext: CoreContext lateinit var coreContext: CoreContext
private fun createConfig(context: Context) {
if (::corePreferences.isInitialized) {
return
}
Factory.instance().setLogCollectionPath(context.filesDir.absolutePath)
Factory.instance().enableLogCollection(LogCollectionState.Enabled)
// For VFS
Factory.instance().setCacheDir(context.cacheDir.absolutePath)
corePreferences = CorePreferences(context)
corePreferences.copyAssetsFromPackage()
if (corePreferences.vfsEnabled) {
CoreContext.activateVFS()
}
val config = Factory.instance().createConfigWithFactory(corePreferences.configPath, corePreferences.factoryConfigPath)
corePreferences.config = config
val appName = context.getString(R.string.app_name)
Factory.instance().setLoggerDomain(appName)
Factory.instance().enableLogcatLogs(corePreferences.logcatLogsOutput)
if (corePreferences.debugLogs) {
Factory.instance().loggingService.setLogLevel(LogLevel.Message)
}
Log.i("[Application] Core config & preferences created")
}
fun ensureCoreExists(
context: Context,
pushReceived: Boolean = false,
service: CoreService? = null,
useAutoStartDescription: Boolean = false
): Boolean {
if (::coreContext.isInitialized && !coreContext.stopped) {
Log.d("[Application] Skipping Core creation (push received? $pushReceived)")
return false
}
Log.i("[Application] Core context is being created ${if (pushReceived) "from push" else ""}")
coreContext = CoreContext(context, corePreferences.config, service, useAutoStartDescription)
coreContext.start()
return true
}
fun contextExists(): Boolean {
return ::coreContext.isInitialized
}
} }
override fun onCreate() { override fun onCreate() {
super.onCreate() super.onCreate()
val appName = getString(R.string.app_name) val context = applicationContext
android.util.Log.i("[$appName]", "Application is being created")
createConfig(applicationContext) val powerManager = context.getSystemService(POWER_SERVICE) as PowerManager
Log.i("[Application] Created") val wakeLock = powerManager.newWakeLock(
PowerManager.PARTIAL_WAKE_LOCK,
"Linphone:AppCreation"
)
wakeLock.acquire(20000L) // 20 seconds
Factory.instance().setLogCollectionPath(context.filesDir.absolutePath)
Factory.instance().enableLogCollection(LogCollectionState.Enabled)
// For VFS
Factory.instance().setCacheDir(context.cacheDir.absolutePath)
corePreferences = CorePreferences(context)
corePreferences.copyAssetsFromPackage()
if (VFS.isEnabled(context)) {
VFS.setup(context)
}
val config = Factory.instance().createConfigWithFactory(
corePreferences.configPath,
corePreferences.factoryConfigPath
)
corePreferences.config = config
val appName = context.getString(R.string.app_name)
Factory.instance().setLoggerDomain(appName)
Factory.instance().loggingService.setLogLevel(LogLevel.Message)
Factory.instance().enableLogcatLogs(corePreferences.printLogsInLogcat)
Log.i("$TAG Report Core preferences initialized")
Compatibility.setupAppStartupListener(context)
coreContext = CoreContext(context)
coreContext.start()
DynamicColors.applyToActivitiesIfAvailable(this)
wakeLock.release()
} }
override fun newImageLoader(): ImageLoader { override fun onTrimMemory(level: Int) {
Log.w("$TAG onTrimMemory called with level [${trimLevelToString(level)}]($level) !")
when (level) {
TRIM_MEMORY_RUNNING_LOW,
TRIM_MEMORY_RUNNING_CRITICAL,
TRIM_MEMORY_MODERATE,
TRIM_MEMORY_COMPLETE -> {
Log.i("$TAG Memory trim required, clearing imageLoader memory cache")
imageLoader.memoryCache?.clear()
}
else -> {}
}
super.onTrimMemory(level)
}
override fun newImageLoader(context: Context): ImageLoader {
// When VFS is enabled, prevent Coil from keeping plain version of files on disk
val diskCachePolicy = if (VFS.isEnabled(applicationContext)) {
CachePolicy.DISABLED
} else {
CachePolicy.ENABLED
}
return ImageLoader.Builder(this) return ImageLoader.Builder(this)
.crossfade(false)
.components { .components {
add(VideoFrameDecoder.Factory()) add(VideoFrameDecoder.Factory())
// add(GifDecoder.Factory) // Do not add it, GIFs are properly rendered without it and adding it breaks resizing...
add(SvgDecoder.Factory()) add(SvgDecoder.Factory())
if (Version.sdkAboveOrEqual(Version.API28_PIE_90)) {
add(ImageDecoderDecoder.Factory())
} else {
add(GifDecoder.Factory())
}
} }
.memoryCache { .memoryCache {
MemoryCache.Builder(this) MemoryCache.Builder()
.maxSizePercent(0.25) .maxSizePercent(context, 0.25)
.build() .build()
} }
.diskCache { .diskCache {
val cache = cacheDir.resolve("image_cache")
DiskCache.Builder() DiskCache.Builder()
.directory(cacheDir.resolve("image_cache")) .directory(cache)
.maxSizePercent(0.02) .maxSizePercent(0.02)
.build() .build()
} }
.networkCachePolicy(CachePolicy.ENABLED)
.diskCachePolicy(diskCachePolicy)
.memoryCachePolicy(CachePolicy.ENABLED)
.build() .build()
} }
private fun trimLevelToString(level: Int): String {
return when (level) {
TRIM_MEMORY_UI_HIDDEN -> "Hidden UI"
TRIM_MEMORY_RUNNING_MODERATE -> "Moderate (Running)"
TRIM_MEMORY_RUNNING_LOW -> "Low"
TRIM_MEMORY_RUNNING_CRITICAL -> "Critical"
TRIM_MEMORY_BACKGROUND -> "Background"
TRIM_MEMORY_MODERATE -> "Moderate"
TRIM_MEMORY_COMPLETE -> "Complete"
else -> level.toString()
}
}
} }

View file

@ -1,137 +0,0 @@
/*
* Copyright (c) 2010-2020 Belledonne Communications SARL.
*
* This file is part of linphone-android
* (see https://www.linphone.org).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.linphone.activities
import android.annotation.SuppressLint
import android.content.pm.ActivityInfo
import android.content.res.Configuration
import android.os.Bundle
import android.util.DisplayMetrics
import android.view.Display
import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.app.AppCompatDelegate
import androidx.lifecycle.lifecycleScope
import androidx.navigation.ActivityNavigator
import androidx.window.layout.FoldingFeature
import androidx.window.layout.WindowInfoTracker
import androidx.window.layout.WindowLayoutInfo
import java.util.*
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import org.linphone.LinphoneApplication.Companion.coreContext
import org.linphone.LinphoneApplication.Companion.corePreferences
import org.linphone.LinphoneApplication.Companion.ensureCoreExists
import org.linphone.R
import org.linphone.core.tools.Log
abstract class GenericActivity : AppCompatActivity() {
private var timer: Timer? = null
private var _isDestructionPending = false
val isDestructionPending: Boolean
get() = _isDestructionPending
open fun onLayoutChanges(foldingFeature: FoldingFeature?) { }
@SuppressLint("SourceLockedOrientationActivity")
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
Log.i("[Generic Activity] Ensuring Core exists")
ensureCoreExists(applicationContext)
lifecycleScope.launch(Dispatchers.Main) {
WindowInfoTracker
.getOrCreate(this@GenericActivity)
.windowLayoutInfo(this@GenericActivity)
.collect { newLayoutInfo ->
updateCurrentLayout(newLayoutInfo)
}
}
requestedOrientation = if (corePreferences.forcePortrait) {
ActivityInfo.SCREEN_ORIENTATION_PORTRAIT
} else {
ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED
}
_isDestructionPending = false
val nightMode = resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK
val darkModeEnabled = corePreferences.darkMode
when (nightMode) {
Configuration.UI_MODE_NIGHT_NO, Configuration.UI_MODE_NIGHT_UNDEFINED -> {
if (darkModeEnabled == 1) {
// Force dark mode
Log.w("[Generic Activity] Forcing night mode")
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES)
_isDestructionPending = true
}
}
Configuration.UI_MODE_NIGHT_YES -> {
if (darkModeEnabled == 0) {
// Force light mode
Log.w("[Generic Activity] Forcing day mode")
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO)
_isDestructionPending = true
}
}
}
updateScreenSize()
}
override fun onResume() {
super.onResume()
// Remove service notification if it has been started by device boot
coreContext.notificationsManager.stopForegroundNotificationIfPossible()
}
override fun finish() {
super.finish()
ActivityNavigator.applyPopAnimationsToPendingTransition(this)
}
fun isTablet(): Boolean {
return resources.getBoolean(R.bool.isTablet)
}
private fun updateScreenSize() {
val metrics = DisplayMetrics()
val display: Display = windowManager.defaultDisplay
display.getRealMetrics(metrics)
val screenWidth = metrics.widthPixels.toFloat()
val screenHeight = metrics.heightPixels.toFloat()
coreContext.screenWidth = screenWidth
coreContext.screenHeight = screenHeight
}
private fun updateCurrentLayout(newLayoutInfo: WindowLayoutInfo) {
if (newLayoutInfo.displayFeatures.isEmpty()) {
onLayoutChanges(null)
} else {
for (feature in newLayoutInfo.displayFeatures) {
val foldingFeature = feature as? FoldingFeature
if (foldingFeature != null) {
onLayoutChanges(foldingFeature)
}
}
}
}
}

View file

@ -1,167 +0,0 @@
/*
* Copyright (c) 2010-2020 Belledonne Communications SARL.
*
* This file is part of linphone-android
* (see https://www.linphone.org).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.linphone.activities
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import androidx.activity.OnBackPressedCallback
import androidx.core.view.doOnPreDraw
import androidx.databinding.DataBindingUtil
import androidx.databinding.ViewDataBinding
import androidx.fragment.app.Fragment
import androidx.lifecycle.ViewModelProvider
import androidx.navigation.fragment.findNavController
import com.google.android.material.transition.MaterialSharedAxis
import org.linphone.LinphoneApplication.Companion.corePreferences
import org.linphone.R
import org.linphone.activities.main.viewmodels.SharedMainViewModel
import org.linphone.core.tools.Log
abstract class GenericFragment<T : ViewDataBinding> : Fragment() {
companion object {
val emptyFragmentsIds = arrayListOf(
R.id.emptyChatFragment,
R.id.emptyContactFragment,
R.id.emptySettingsFragment,
R.id.emptyCallHistoryFragment
)
}
private var _binding: T? = null
protected val binding get() = _binding!!
protected var useMaterialSharedAxisXForwardAnimation = true
protected lateinit var sharedViewModel: SharedMainViewModel
protected fun isSharedViewModelInitialized(): Boolean {
return ::sharedViewModel.isInitialized
}
protected fun isBindingAvailable(): Boolean {
return _binding != null
}
private fun getFragmentRealClassName(): String {
return this.javaClass.name
}
private val onBackPressedCallback = object : OnBackPressedCallback(false) {
override fun handleOnBackPressed() {
try {
val navController = findNavController()
Log.d("[Generic Fragment] ${getFragmentRealClassName()} handleOnBackPressed")
if (!navController.popBackStack()) {
Log.d("[Generic Fragment] ${getFragmentRealClassName()} couldn't pop")
if (!navController.navigateUp()) {
Log.d("[Generic Fragment] ${getFragmentRealClassName()} couldn't navigate up")
// Disable this callback & start a new back press event
isEnabled = false
goBack()
}
}
} catch (ise: IllegalStateException) {
Log.e("[Generic Fragment] ${getFragmentRealClassName()} Can't go back: $ise")
}
}
}
abstract fun getLayoutId(): Int
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
sharedViewModel = requireActivity().run {
ViewModelProvider(this)[SharedMainViewModel::class.java]
}
sharedViewModel.isSlidingPaneSlideable.observe(viewLifecycleOwner) {
Log.d("[Generic Fragment] ${getFragmentRealClassName()} shared main VM sliding pane has changed")
onBackPressedCallback.isEnabled = backPressedCallBackEnabled()
}
_binding = DataBindingUtil.inflate(inflater, getLayoutId(), container, false)
return _binding!!.root
}
override fun onStart() {
super.onStart()
if (useMaterialSharedAxisXForwardAnimation && corePreferences.enableAnimations) {
enterTransition = MaterialSharedAxis(MaterialSharedAxis.X, true)
reenterTransition = MaterialSharedAxis(MaterialSharedAxis.X, true)
returnTransition = MaterialSharedAxis(MaterialSharedAxis.X, false)
exitTransition = MaterialSharedAxis(MaterialSharedAxis.X, false)
postponeEnterTransition()
binding.root.doOnPreDraw { startPostponedEnterTransition() }
}
setupBackPressCallback()
}
override fun onDestroyView() {
super.onDestroyView()
onBackPressedCallback.remove()
_binding = null
}
protected fun goBack() {
requireActivity().onBackPressedDispatcher.onBackPressed()
}
private fun setupBackPressCallback() {
Log.d("[Generic Fragment] ${getFragmentRealClassName()} setupBackPressCallback")
val backButton = binding.root.findViewById<ImageView>(R.id.back)
if (backButton != null) {
Log.d("[Generic Fragment] ${getFragmentRealClassName()} found back button")
// If popping navigation back stack entry would bring us to an "empty" fragment
// then don't do it if sliding pane layout isn't "flat"
onBackPressedCallback.isEnabled = backPressedCallBackEnabled()
backButton.setOnClickListener { goBack() }
} else {
onBackPressedCallback.isEnabled = false
}
requireActivity().onBackPressedDispatcher.addCallback(viewLifecycleOwner, onBackPressedCallback)
}
private fun backPressedCallBackEnabled(): Boolean {
// This allow to navigate a SlidingPane child nav graph.
// This only concerns fragments for which the nav graph is inside a SlidingPane layout.
// In our case it's all graphs except the main one.
if (findNavController().graph.id == R.id.main_nav_graph_xml) return false
val isSlidingPaneFlat = sharedViewModel.isSlidingPaneSlideable.value == false
Log.d("[Generic Fragment] ${getFragmentRealClassName()} isSlidingPaneFlat ? $isSlidingPaneFlat")
val isPreviousFragmentEmpty = findNavController().previousBackStackEntry?.destination?.id in emptyFragmentsIds
Log.d("[Generic Fragment] ${getFragmentRealClassName()} isPreviousFragmentEmpty ? $isPreviousFragmentEmpty")
val popBackStack = isSlidingPaneFlat || !isPreviousFragmentEmpty
Log.d("[Generic Fragment] ${getFragmentRealClassName()} popBackStack ? $popBackStack")
return popBackStack
}
}

File diff suppressed because it is too large Load diff

View file

@ -1,89 +0,0 @@
/*
* Copyright (c) 2010-2020 Belledonne Communications SARL.
*
* This file is part of linphone-android
* (see https://www.linphone.org).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.linphone.activities
import android.content.Context
import android.os.Bundle
import android.os.PowerManager
import android.os.PowerManager.RELEASE_FLAG_WAIT_FOR_NO_PROXIMITY
import org.linphone.LinphoneApplication.Companion.coreContext
import org.linphone.core.tools.Log
abstract class ProximitySensorActivity : GenericActivity() {
private lateinit var proximityWakeLock: PowerManager.WakeLock
private var proximitySensorEnabled = false
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val powerManager = getSystemService(Context.POWER_SERVICE) as PowerManager
if (!powerManager.isWakeLockLevelSupported(PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK)) {
Log.w("[Proximity Sensor Activity] PROXIMITY_SCREEN_OFF_WAKE_LOCK isn't supported on this device!")
}
proximityWakeLock = powerManager.newWakeLock(
PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK,
"$packageName;proximity_sensor"
)
}
override fun onResume() {
super.onResume()
if (coreContext.core.callsNb > 0) {
val videoEnabled = coreContext.core.currentCall?.currentParams?.isVideoEnabled ?: false
enableProximitySensor(!videoEnabled)
}
}
override fun onPause() {
enableProximitySensor(false)
super.onPause()
}
override fun onDestroy() {
enableProximitySensor(false)
super.onDestroy()
}
protected fun enableProximitySensor(enable: Boolean) {
if (enable) {
if (!proximitySensorEnabled) {
Log.i("[Proximity Sensor Activity] Enabling proximity sensor turning off screen")
if (!proximityWakeLock.isHeld) {
Log.i("[Proximity Sensor Activity] Acquiring PROXIMITY_SCREEN_OFF_WAKE_LOCK")
proximityWakeLock.acquire()
}
proximitySensorEnabled = true
}
} else {
if (proximitySensorEnabled) {
Log.i("[Proximity Sensor Activity] Disabling proximity sensor turning off screen")
if (proximityWakeLock.isHeld) {
Log.i("[Proximity Sensor Activity] Releasing PROXIMITY_SCREEN_OFF_WAKE_LOCK")
proximityWakeLock.release(RELEASE_FLAG_WAIT_FOR_NO_PROXIMITY)
}
proximitySensorEnabled = false
}
}
}
}

View file

@ -1,65 +0,0 @@
/*
* Copyright (c) 2010-2020 Belledonne Communications SARL.
*
* This file is part of linphone-android
* (see https://www.linphone.org).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.linphone.activities.assistant
import android.os.Bundle
import androidx.annotation.StringRes
import androidx.coordinatorlayout.widget.CoordinatorLayout
import androidx.lifecycle.ViewModelProvider
import com.google.android.material.snackbar.Snackbar
import org.linphone.LinphoneApplication.Companion.corePreferences
import org.linphone.R
import org.linphone.activities.GenericActivity
import org.linphone.activities.SnackBarActivity
import org.linphone.activities.assistant.viewmodels.SharedAssistantViewModel
class AssistantActivity : GenericActivity(), SnackBarActivity {
private lateinit var sharedViewModel: SharedAssistantViewModel
private lateinit var coordinator: CoordinatorLayout
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.assistant_activity)
sharedViewModel = ViewModelProvider(this)[SharedAssistantViewModel::class.java]
coordinator = findViewById(R.id.coordinator)
corePreferences.firstStart = false
}
override fun showSnackBar(@StringRes resourceId: Int) {
Snackbar.make(coordinator, resourceId, Snackbar.LENGTH_LONG).show()
}
override fun showSnackBar(@StringRes resourceId: Int, action: Int, listener: () -> Unit) {
Snackbar
.make(findViewById(R.id.coordinator), resourceId, Snackbar.LENGTH_LONG)
.setAction(action) {
listener()
}
.show()
}
override fun showSnackBar(message: String) {
Snackbar.make(coordinator, message, Snackbar.LENGTH_LONG).show()
}
}

View file

@ -1,95 +0,0 @@
/*
* Copyright (c) 2010-2020 Belledonne Communications SARL.
*
* This file is part of linphone-android
* (see https://www.linphone.org).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.linphone.activities.assistant.adapters
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.BaseAdapter
import android.widget.Filter
import android.widget.Filterable
import android.widget.TextView
import kotlin.collections.ArrayList
import org.linphone.R
import org.linphone.core.DialPlan
import org.linphone.core.Factory
class CountryPickerAdapter : BaseAdapter(), Filterable {
private var countries: ArrayList<DialPlan>
init {
val dialPlans = Factory.instance().dialPlans
countries = arrayListOf()
countries.addAll(dialPlans)
}
override fun getView(position: Int, convertView: View?, parent: ViewGroup): View {
val view: View = convertView ?: LayoutInflater.from(parent.context).inflate(R.layout.assistant_country_picker_cell, parent, false)
val dialPlan: DialPlan = countries[position]
val name = view.findViewById<TextView>(R.id.country_name)
name.text = dialPlan.country
val dialCode = view.findViewById<TextView>(R.id.country_prefix)
dialCode.text = String.format("(%s)", dialPlan.countryCallingCode)
view.tag = dialPlan
return view
}
override fun getItem(position: Int): DialPlan {
return countries[position]
}
override fun getItemId(position: Int): Long {
return position.toLong()
}
override fun getCount(): Int {
return countries.size
}
override fun getFilter(): Filter {
return object : Filter() {
override fun performFiltering(constraint: CharSequence): FilterResults {
val filteredCountries = arrayListOf<DialPlan>()
for (dialPlan in Factory.instance().dialPlans) {
if (dialPlan.country.contains(constraint, ignoreCase = true) ||
dialPlan.countryCallingCode.contains(constraint)
) {
filteredCountries.add(dialPlan)
}
}
val filterResults = FilterResults()
filterResults.values = filteredCountries
return filterResults
}
@Suppress("UNCHECKED_CAST")
override fun publishResults(
constraint: CharSequence,
results: FilterResults
) {
countries = results.values as ArrayList<DialPlan>
notifyDataSetChanged()
}
}
}
}

View file

@ -1,86 +0,0 @@
/*
* Copyright (c) 2010-2020 Belledonne Communications SARL.
*
* This file is part of linphone-android
* (see https://www.linphone.org).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.linphone.activities.assistant.fragments
import android.content.pm.PackageManager
import androidx.databinding.ViewDataBinding
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import org.linphone.R
import org.linphone.activities.GenericFragment
import org.linphone.activities.assistant.viewmodels.AbstractPhoneViewModel
import org.linphone.compatibility.Compatibility
import org.linphone.core.tools.Log
import org.linphone.utils.PermissionHelper
import org.linphone.utils.PhoneNumberUtils
abstract class AbstractPhoneFragment<T : ViewDataBinding> : GenericFragment<T>() {
companion object {
const val READ_PHONE_STATE_PERMISSION_REQUEST_CODE = 0
}
abstract val viewModel: AbstractPhoneViewModel
@Deprecated("Deprecated in Java")
override fun onRequestPermissionsResult(
requestCode: Int,
permissions: Array<out String>,
grantResults: IntArray
) {
if (requestCode == READ_PHONE_STATE_PERMISSION_REQUEST_CODE) {
if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
Log.i("[Assistant] READ_PHONE_STATE/READ_PHONE_NUMBERS permission granted")
updateFromDeviceInfo()
} else {
Log.w("[Assistant] READ_PHONE_STATE/READ_PHONE_NUMBERS permission denied")
}
}
}
protected fun checkPermissions() {
if (!resources.getBoolean(R.bool.isTablet)) {
if (!PermissionHelper.get().hasReadPhoneStateOrPhoneNumbersPermission()) {
Log.i("[Assistant] Asking for READ_PHONE_STATE/READ_PHONE_NUMBERS permission")
Compatibility.requestReadPhoneStateOrNumbersPermission(this, READ_PHONE_STATE_PERMISSION_REQUEST_CODE)
} else {
updateFromDeviceInfo()
}
}
}
private fun updateFromDeviceInfo() {
val phoneNumber = PhoneNumberUtils.getDevicePhoneNumber(requireContext())
val dialPlan = PhoneNumberUtils.getDialPlanForCurrentCountry(requireContext())
viewModel.updateFromPhoneNumberAndOrDialPlan(phoneNumber, dialPlan)
}
protected fun showPhoneNumberInfoDialog() {
MaterialAlertDialogBuilder(requireContext())
.setTitle(getString(R.string.assistant_phone_number_info_title))
.setMessage(
getString(R.string.assistant_phone_number_link_info_content) + "\n" +
getString(
R.string.assistant_phone_number_link_info_content_already_account
)
)
.setNegativeButton(getString(R.string.dialog_ok), null)
.show()
}
}

View file

@ -1,140 +0,0 @@
/*
* Copyright (c) 2010-2020 Belledonne Communications SARL.
*
* This file is part of linphone-android
* (see https://www.linphone.org).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.linphone.activities.assistant.fragments
import android.app.Dialog
import android.content.Intent
import android.net.Uri
import android.os.Bundle
import android.view.View
import androidx.lifecycle.ViewModelProvider
import org.linphone.LinphoneApplication.Companion.coreContext
import org.linphone.R
import org.linphone.activities.assistant.AssistantActivity
import org.linphone.activities.assistant.viewmodels.AccountLoginViewModel
import org.linphone.activities.assistant.viewmodels.AccountLoginViewModelFactory
import org.linphone.activities.assistant.viewmodels.SharedAssistantViewModel
import org.linphone.activities.main.viewmodels.DialogViewModel
import org.linphone.activities.navigateToEchoCancellerCalibration
import org.linphone.activities.navigateToPhoneAccountValidation
import org.linphone.databinding.AssistantAccountLoginFragmentBinding
import org.linphone.utils.DialogUtils
class AccountLoginFragment : AbstractPhoneFragment<AssistantAccountLoginFragmentBinding>() {
override lateinit var viewModel: AccountLoginViewModel
private lateinit var sharedAssistantViewModel: SharedAssistantViewModel
override fun getLayoutId(): Int = R.layout.assistant_account_login_fragment
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding.lifecycleOwner = viewLifecycleOwner
sharedAssistantViewModel = requireActivity().run {
ViewModelProvider(this)[SharedAssistantViewModel::class.java]
}
viewModel = ViewModelProvider(
this,
AccountLoginViewModelFactory(sharedAssistantViewModel.getAccountCreator())
)[AccountLoginViewModel::class.java]
binding.viewModel = viewModel
if (resources.getBoolean(R.bool.isTablet)) {
viewModel.loginWithUsernamePassword.value = true
}
binding.setInfoClickListener {
showPhoneNumberInfoDialog()
}
binding.setSelectCountryClickListener {
CountryPickerFragment(viewModel).show(childFragmentManager, "CountryPicker")
}
binding.setForgotPasswordClickListener {
val intent = Intent(Intent.ACTION_VIEW)
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
intent.data = Uri.parse(getString(R.string.assistant_forgotten_password_link))
startActivity(intent)
}
viewModel.goToSmsValidationEvent.observe(
viewLifecycleOwner
) {
it.consume {
val args = Bundle()
args.putBoolean("IsLogin", true)
args.putString("PhoneNumber", viewModel.accountCreator.phoneNumber)
navigateToPhoneAccountValidation(args)
}
}
viewModel.leaveAssistantEvent.observe(
viewLifecycleOwner
) {
it.consume {
coreContext.newAccountConfigured(true)
if (coreContext.core.isEchoCancellerCalibrationRequired) {
navigateToEchoCancellerCalibration()
} else {
requireActivity().finish()
}
}
}
viewModel.invalidCredentialsEvent.observe(
viewLifecycleOwner
) {
it.consume {
val dialogViewModel =
DialogViewModel(getString(R.string.assistant_error_invalid_credentials))
val dialog: Dialog = DialogUtils.getDialog(requireContext(), dialogViewModel)
dialogViewModel.showCancelButton {
viewModel.removeInvalidProxyConfig()
dialog.dismiss()
}
dialogViewModel.showDeleteButton(
{
viewModel.continueEvenIfInvalidCredentials()
dialog.dismiss()
},
getString(R.string.assistant_continue_even_if_credentials_invalid)
)
dialog.show()
}
}
viewModel.onErrorEvent.observe(
viewLifecycleOwner
) {
it.consume { message ->
(requireActivity() as AssistantActivity).showSnackBar(message)
}
}
checkPermissions()
}
}

View file

@ -1,85 +0,0 @@
/*
* Copyright (c) 2010-2020 Belledonne Communications SARL.
*
* This file is part of linphone-android
* (see https://www.linphone.org).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.linphone.activities.assistant.fragments
import android.os.Bundle
import android.text.Editable
import android.text.TextWatcher
import android.view.*
import androidx.fragment.app.DialogFragment
import org.linphone.R
import org.linphone.activities.assistant.adapters.CountryPickerAdapter
import org.linphone.core.DialPlan
import org.linphone.databinding.AssistantCountryPickerFragmentBinding
class CountryPickerFragment(private val listener: CountryPickedListener) : DialogFragment() {
private var _binding: AssistantCountryPickerFragmentBinding? = null
private val binding get() = _binding!!
private lateinit var adapter: CountryPickerAdapter
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setStyle(STYLE_NO_TITLE, R.style.assistant_country_dialog_style)
}
override fun onDestroyView() {
super.onDestroyView()
_binding = null
}
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
_binding = AssistantCountryPickerFragmentBinding.inflate(inflater, container, false)
adapter = CountryPickerAdapter()
binding.countryList.adapter = adapter
binding.countryList.setOnItemClickListener { _, _, position, _ ->
if (position >= 0 && position < adapter.count) {
val dialPlan = adapter.getItem(position)
listener.onCountryClicked(dialPlan)
}
dismiss()
}
binding.searchCountry.addTextChangedListener(object : TextWatcher {
override fun afterTextChanged(s: Editable?) {
adapter.filter.filter(s)
}
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) { }
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) { }
})
binding.setCancelClickListener {
dismiss()
}
return binding.root
}
interface CountryPickedListener {
fun onCountryClicked(dialPlan: DialPlan)
}
}

View file

@ -1,84 +0,0 @@
/*
* Copyright (c) 2010-2020 Belledonne Communications SARL.
*
* This file is part of linphone-android
* (see https://www.linphone.org).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.linphone.activities.assistant.fragments
import android.content.pm.PackageManager
import android.os.Bundle
import android.view.View
import androidx.lifecycle.ViewModelProvider
import org.linphone.R
import org.linphone.activities.GenericFragment
import org.linphone.activities.assistant.viewmodels.EchoCancellerCalibrationViewModel
import org.linphone.core.tools.Log
import org.linphone.databinding.AssistantEchoCancellerCalibrationFragmentBinding
import org.linphone.utils.PermissionHelper
class EchoCancellerCalibrationFragment : GenericFragment<AssistantEchoCancellerCalibrationFragmentBinding>() {
companion object {
const val RECORD_AUDIO_PERMISSION_REQUEST_CODE = 0
}
private lateinit var viewModel: EchoCancellerCalibrationViewModel
override fun getLayoutId(): Int = R.layout.assistant_echo_canceller_calibration_fragment
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding.lifecycleOwner = viewLifecycleOwner
viewModel = ViewModelProvider(this)[EchoCancellerCalibrationViewModel::class.java]
binding.viewModel = viewModel
viewModel.echoCalibrationTerminated.observe(
viewLifecycleOwner
) {
it.consume {
requireActivity().finish()
}
}
if (!PermissionHelper.required(requireContext()).hasRecordAudioPermission()) {
Log.i("[Echo Canceller Calibration] Asking for RECORD_AUDIO permission")
requestPermissions(arrayOf(android.Manifest.permission.RECORD_AUDIO), RECORD_AUDIO_PERMISSION_REQUEST_CODE)
} else {
viewModel.startEchoCancellerCalibration()
}
}
@Deprecated("Deprecated in Java")
override fun onRequestPermissionsResult(
requestCode: Int,
permissions: Array<out String>,
grantResults: IntArray
) {
if (requestCode == RECORD_AUDIO_PERMISSION_REQUEST_CODE) {
val granted =
grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED
if (granted) {
Log.i("[Echo Canceller Calibration] RECORD_AUDIO permission granted")
viewModel.startEchoCancellerCalibration()
} else {
Log.w("[Echo Canceller Calibration] RECORD_AUDIO permission denied")
requireActivity().finish()
}
}
}
}

View file

@ -1,68 +0,0 @@
/*
* Copyright (c) 2010-2020 Belledonne Communications SARL.
*
* This file is part of linphone-android
* (see https://www.linphone.org).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.linphone.activities.assistant.fragments
import android.os.Bundle
import android.view.View
import androidx.lifecycle.ViewModelProvider
import org.linphone.R
import org.linphone.activities.GenericFragment
import org.linphone.activities.assistant.AssistantActivity
import org.linphone.activities.assistant.viewmodels.EmailAccountCreationViewModel
import org.linphone.activities.assistant.viewmodels.EmailAccountCreationViewModelFactory
import org.linphone.activities.assistant.viewmodels.SharedAssistantViewModel
import org.linphone.activities.navigateToEmailAccountValidation
import org.linphone.databinding.AssistantEmailAccountCreationFragmentBinding
class EmailAccountCreationFragment : GenericFragment<AssistantEmailAccountCreationFragmentBinding>() {
private lateinit var sharedAssistantViewModel: SharedAssistantViewModel
private lateinit var viewModel: EmailAccountCreationViewModel
override fun getLayoutId(): Int = R.layout.assistant_email_account_creation_fragment
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding.lifecycleOwner = viewLifecycleOwner
sharedAssistantViewModel = requireActivity().run {
ViewModelProvider(this)[SharedAssistantViewModel::class.java]
}
viewModel = ViewModelProvider(this, EmailAccountCreationViewModelFactory(sharedAssistantViewModel.getAccountCreator()))[EmailAccountCreationViewModel::class.java]
binding.viewModel = viewModel
viewModel.goToEmailValidationEvent.observe(
viewLifecycleOwner
) {
it.consume {
navigateToEmailAccountValidation()
}
}
viewModel.onErrorEvent.observe(
viewLifecycleOwner
) {
it.consume { message ->
(requireActivity() as AssistantActivity).showSnackBar(message)
}
}
}
}

View file

@ -1,73 +0,0 @@
/*
* Copyright (c) 2010-2020 Belledonne Communications SARL.
*
* This file is part of linphone-android
* (see https://www.linphone.org).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.linphone.activities.assistant.fragments
import android.os.Bundle
import android.view.View
import androidx.lifecycle.ViewModelProvider
import org.linphone.LinphoneApplication.Companion.coreContext
import org.linphone.R
import org.linphone.activities.GenericFragment
import org.linphone.activities.assistant.AssistantActivity
import org.linphone.activities.assistant.viewmodels.*
import org.linphone.activities.navigateToAccountLinking
import org.linphone.databinding.AssistantEmailAccountValidationFragmentBinding
class EmailAccountValidationFragment : GenericFragment<AssistantEmailAccountValidationFragmentBinding>() {
private lateinit var sharedAssistantViewModel: SharedAssistantViewModel
private lateinit var viewModel: EmailAccountValidationViewModel
override fun getLayoutId(): Int = R.layout.assistant_email_account_validation_fragment
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding.lifecycleOwner = viewLifecycleOwner
sharedAssistantViewModel = requireActivity().run {
ViewModelProvider(this)[SharedAssistantViewModel::class.java]
}
viewModel = ViewModelProvider(this, EmailAccountValidationViewModelFactory(sharedAssistantViewModel.getAccountCreator()))[EmailAccountValidationViewModel::class.java]
binding.viewModel = viewModel
viewModel.leaveAssistantEvent.observe(
viewLifecycleOwner
) {
it.consume {
coreContext.newAccountConfigured(true)
val args = Bundle()
args.putBoolean("AllowSkip", true)
args.putString("Username", viewModel.accountCreator.username)
args.putString("Password", viewModel.accountCreator.password)
navigateToAccountLinking(args)
}
}
viewModel.onErrorEvent.observe(
viewLifecycleOwner
) {
it.consume { message ->
(requireActivity() as AssistantActivity).showSnackBar(message)
}
}
}
}

View file

@ -1,105 +0,0 @@
/*
* Copyright (c) 2010-2020 Belledonne Communications SARL.
*
* This file is part of linphone-android
* (see https://www.linphone.org).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.linphone.activities.assistant.fragments
import android.app.Dialog
import android.os.Bundle
import android.view.View
import androidx.lifecycle.ViewModelProvider
import org.linphone.LinphoneApplication.Companion.coreContext
import org.linphone.LinphoneApplication.Companion.corePreferences
import org.linphone.R
import org.linphone.activities.GenericFragment
import org.linphone.activities.assistant.AssistantActivity
import org.linphone.activities.assistant.viewmodels.GenericLoginViewModel
import org.linphone.activities.assistant.viewmodels.GenericLoginViewModelFactory
import org.linphone.activities.assistant.viewmodels.SharedAssistantViewModel
import org.linphone.activities.main.viewmodels.DialogViewModel
import org.linphone.activities.navigateToEchoCancellerCalibration
import org.linphone.databinding.AssistantGenericAccountLoginFragmentBinding
import org.linphone.utils.DialogUtils
class GenericAccountLoginFragment : GenericFragment<AssistantGenericAccountLoginFragmentBinding>() {
private lateinit var sharedAssistantViewModel: SharedAssistantViewModel
private lateinit var viewModel: GenericLoginViewModel
override fun getLayoutId(): Int = R.layout.assistant_generic_account_login_fragment
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding.lifecycleOwner = viewLifecycleOwner
sharedAssistantViewModel = requireActivity().run {
ViewModelProvider(this)[SharedAssistantViewModel::class.java]
}
viewModel = ViewModelProvider(this, GenericLoginViewModelFactory(sharedAssistantViewModel.getAccountCreator(true)))[GenericLoginViewModel::class.java]
binding.viewModel = viewModel
viewModel.leaveAssistantEvent.observe(
viewLifecycleOwner
) {
it.consume {
val isLinphoneAccount = viewModel.domain.value.orEmpty() == corePreferences.defaultDomain
coreContext.newAccountConfigured(isLinphoneAccount)
if (coreContext.core.isEchoCancellerCalibrationRequired) {
navigateToEchoCancellerCalibration()
} else {
requireActivity().finish()
}
}
}
viewModel.invalidCredentialsEvent.observe(
viewLifecycleOwner
) {
it.consume {
val dialogViewModel =
DialogViewModel(getString(R.string.assistant_error_invalid_credentials))
val dialog: Dialog = DialogUtils.getDialog(requireContext(), dialogViewModel)
dialogViewModel.showCancelButton {
viewModel.removeInvalidProxyConfig()
dialog.dismiss()
}
dialogViewModel.showDeleteButton(
{
viewModel.continueEvenIfInvalidCredentials()
dialog.dismiss()
},
getString(R.string.assistant_continue_even_if_credentials_invalid)
)
dialog.show()
}
}
viewModel.onErrorEvent.observe(
viewLifecycleOwner
) {
it.consume { message ->
(requireActivity() as AssistantActivity).showSnackBar(message)
}
}
}
}

View file

@ -1,84 +0,0 @@
/*
* Copyright (c) 2010-2020 Belledonne Communications SARL.
*
* This file is part of linphone-android
* (see https://www.linphone.org).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.linphone.activities.assistant.fragments
import android.os.Bundle
import android.view.View
import androidx.lifecycle.ViewModelProvider
import org.linphone.R
import org.linphone.activities.assistant.AssistantActivity
import org.linphone.activities.assistant.viewmodels.PhoneAccountCreationViewModel
import org.linphone.activities.assistant.viewmodels.PhoneAccountCreationViewModelFactory
import org.linphone.activities.assistant.viewmodels.SharedAssistantViewModel
import org.linphone.activities.navigateToPhoneAccountValidation
import org.linphone.databinding.AssistantPhoneAccountCreationFragmentBinding
class PhoneAccountCreationFragment :
AbstractPhoneFragment<AssistantPhoneAccountCreationFragmentBinding>() {
private lateinit var sharedAssistantViewModel: SharedAssistantViewModel
override lateinit var viewModel: PhoneAccountCreationViewModel
override fun getLayoutId(): Int = R.layout.assistant_phone_account_creation_fragment
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding.lifecycleOwner = viewLifecycleOwner
sharedAssistantViewModel = requireActivity().run {
ViewModelProvider(this)[SharedAssistantViewModel::class.java]
}
viewModel = ViewModelProvider(
this,
PhoneAccountCreationViewModelFactory(sharedAssistantViewModel.getAccountCreator())
)[PhoneAccountCreationViewModel::class.java]
binding.viewModel = viewModel
binding.setInfoClickListener {
showPhoneNumberInfoDialog()
}
binding.setSelectCountryClickListener {
CountryPickerFragment(viewModel).show(childFragmentManager, "CountryPicker")
}
viewModel.goToSmsValidationEvent.observe(
viewLifecycleOwner
) {
it.consume {
val args = Bundle()
args.putBoolean("IsCreation", true)
args.putString("PhoneNumber", viewModel.accountCreator.phoneNumber)
navigateToPhoneAccountValidation(args)
}
}
viewModel.onErrorEvent.observe(
viewLifecycleOwner
) {
it.consume { message ->
(requireActivity() as AssistantActivity).showSnackBar(message)
}
}
checkPermissions()
}
}

View file

@ -1,107 +0,0 @@
/*
* Copyright (c) 2010-2020 Belledonne Communications SARL.
*
* This file is part of linphone-android
* (see https://www.linphone.org).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.linphone.activities.assistant.fragments
import android.os.Bundle
import android.view.View
import androidx.lifecycle.ViewModelProvider
import org.linphone.LinphoneApplication.Companion.coreContext
import org.linphone.R
import org.linphone.activities.SnackBarActivity
import org.linphone.activities.assistant.viewmodels.*
import org.linphone.activities.navigateToEchoCancellerCalibration
import org.linphone.activities.navigateToPhoneAccountValidation
import org.linphone.core.tools.Log
import org.linphone.databinding.AssistantPhoneAccountLinkingFragmentBinding
class PhoneAccountLinkingFragment : AbstractPhoneFragment<AssistantPhoneAccountLinkingFragmentBinding>() {
private lateinit var sharedAssistantViewModel: SharedAssistantViewModel
override lateinit var viewModel: PhoneAccountLinkingViewModel
override fun getLayoutId(): Int = R.layout.assistant_phone_account_linking_fragment
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding.lifecycleOwner = viewLifecycleOwner
sharedAssistantViewModel = requireActivity().run {
ViewModelProvider(this)[SharedAssistantViewModel::class.java]
}
val accountCreator = sharedAssistantViewModel.getAccountCreator()
viewModel = ViewModelProvider(this, PhoneAccountLinkingViewModelFactory(accountCreator))[PhoneAccountLinkingViewModel::class.java]
binding.viewModel = viewModel
val username = arguments?.getString("Username")
Log.i("[Phone Account Linking] username to link is $username")
viewModel.username.value = username
val password = arguments?.getString("Password")
accountCreator.password = password
val ha1 = arguments?.getString("HA1")
accountCreator.ha1 = ha1
val allowSkip = arguments?.getBoolean("AllowSkip", false)
viewModel.allowSkip.value = allowSkip
binding.setInfoClickListener {
showPhoneNumberInfoDialog()
}
binding.setSelectCountryClickListener {
CountryPickerFragment(viewModel).show(childFragmentManager, "CountryPicker")
}
viewModel.goToSmsValidationEvent.observe(
viewLifecycleOwner
) {
it.consume {
val args = Bundle()
args.putBoolean("IsLinking", true)
args.putString("PhoneNumber", viewModel.accountCreator.phoneNumber)
navigateToPhoneAccountValidation(args)
}
}
viewModel.leaveAssistantEvent.observe(
viewLifecycleOwner
) {
it.consume {
if (coreContext.core.isEchoCancellerCalibrationRequired) {
navigateToEchoCancellerCalibration()
} else {
requireActivity().finish()
}
}
}
viewModel.onErrorEvent.observe(
viewLifecycleOwner
) {
it.consume { message ->
(requireActivity() as SnackBarActivity).showSnackBar(message)
}
}
checkPermissions()
}
}

View file

@ -1,116 +0,0 @@
/*
* Copyright (c) 2010-2020 Belledonne Communications SARL.
*
* This file is part of linphone-android
* (see https://www.linphone.org).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.linphone.activities.assistant.fragments
import android.content.ClipboardManager
import android.content.Context.CLIPBOARD_SERVICE
import android.os.Bundle
import android.view.View
import androidx.lifecycle.ViewModelProvider
import androidx.navigation.fragment.findNavController
import org.linphone.LinphoneApplication.Companion.coreContext
import org.linphone.R
import org.linphone.activities.GenericFragment
import org.linphone.activities.SnackBarActivity
import org.linphone.activities.assistant.viewmodels.PhoneAccountValidationViewModel
import org.linphone.activities.assistant.viewmodels.PhoneAccountValidationViewModelFactory
import org.linphone.activities.assistant.viewmodels.SharedAssistantViewModel
import org.linphone.activities.navigateToAccountSettings
import org.linphone.activities.navigateToEchoCancellerCalibration
import org.linphone.core.tools.Log
import org.linphone.databinding.AssistantPhoneAccountValidationFragmentBinding
class PhoneAccountValidationFragment : GenericFragment<AssistantPhoneAccountValidationFragmentBinding>() {
private lateinit var sharedAssistantViewModel: SharedAssistantViewModel
private lateinit var viewModel: PhoneAccountValidationViewModel
override fun getLayoutId(): Int = R.layout.assistant_phone_account_validation_fragment
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding.lifecycleOwner = viewLifecycleOwner
sharedAssistantViewModel = requireActivity().run {
ViewModelProvider(this)[SharedAssistantViewModel::class.java]
}
viewModel = ViewModelProvider(this, PhoneAccountValidationViewModelFactory(sharedAssistantViewModel.getAccountCreator()))[PhoneAccountValidationViewModel::class.java]
binding.viewModel = viewModel
viewModel.phoneNumber.value = arguments?.getString("PhoneNumber")
viewModel.isLogin.value = arguments?.getBoolean("IsLogin", false)
viewModel.isCreation.value = arguments?.getBoolean("IsCreation", false)
viewModel.isLinking.value = arguments?.getBoolean("IsLinking", false)
viewModel.leaveAssistantEvent.observe(
viewLifecycleOwner
) {
it.consume {
when {
viewModel.isLogin.value == true || viewModel.isCreation.value == true -> {
coreContext.newAccountConfigured(true)
if (coreContext.core.isEchoCancellerCalibrationRequired) {
navigateToEchoCancellerCalibration()
} else {
requireActivity().finish()
}
}
viewModel.isLinking.value == true -> {
if (findNavController().graph.id == R.id.settings_nav_graph_xml) {
val args = Bundle()
args.putString(
"Identity",
"sip:${viewModel.accountCreator.username}@${viewModel.accountCreator.domain}"
)
navigateToAccountSettings(args)
} else {
requireActivity().finish()
}
}
}
}
}
viewModel.onErrorEvent.observe(
viewLifecycleOwner
) {
it.consume { message ->
(requireActivity() as SnackBarActivity).showSnackBar(message)
}
}
// This won't work starting Android 10 as clipboard access is denied unless app has focus,
// which won't be the case when the SMS arrives unless it is added into clipboard from a notification
val clipboard = requireContext().getSystemService(CLIPBOARD_SERVICE) as ClipboardManager
clipboard.addPrimaryClipChangedListener {
val data = clipboard.primaryClip
if (data != null && data.itemCount > 0) {
val clip = data.getItemAt(0).text.toString()
if (clip.length == 4) {
Log.i("[Assistant] [Phone Account Validation] Found 4 digits as primary clip in clipboard, using it and clear it")
viewModel.code.value = clip
clipboard.clearPrimaryClip()
}
}
}
}
}

View file

@ -1,109 +0,0 @@
/*
* Copyright (c) 2010-2020 Belledonne Communications SARL.
*
* This file is part of linphone-android
* (see https://www.linphone.org).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.linphone.activities.assistant.fragments
import android.content.pm.PackageManager
import android.os.Bundle
import android.view.View
import androidx.lifecycle.ViewModelProvider
import androidx.navigation.fragment.findNavController
import org.linphone.LinphoneApplication.Companion.coreContext
import org.linphone.R
import org.linphone.activities.GenericFragment
import org.linphone.activities.assistant.viewmodels.QrCodeViewModel
import org.linphone.activities.assistant.viewmodels.SharedAssistantViewModel
import org.linphone.core.tools.Log
import org.linphone.databinding.AssistantQrCodeFragmentBinding
import org.linphone.utils.PermissionHelper
class QrCodeFragment : GenericFragment<AssistantQrCodeFragmentBinding>() {
companion object {
const val CAMERA_PERMISSION_REQUEST_CODE = 0
}
private lateinit var sharedAssistantViewModel: SharedAssistantViewModel
private lateinit var viewModel: QrCodeViewModel
override fun getLayoutId(): Int = R.layout.assistant_qr_code_fragment
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding.lifecycleOwner = viewLifecycleOwner
sharedAssistantViewModel = requireActivity().run {
ViewModelProvider(this)[SharedAssistantViewModel::class.java]
}
viewModel = ViewModelProvider(this)[QrCodeViewModel::class.java]
binding.viewModel = viewModel
viewModel.qrCodeFoundEvent.observe(
viewLifecycleOwner
) {
it.consume { url ->
sharedAssistantViewModel.remoteProvisioningUrl.value = url
findNavController().navigateUp()
}
}
viewModel.setBackCamera()
if (!PermissionHelper.required(requireContext()).hasCameraPermission()) {
Log.i("[QR Code] Asking for CAMERA permission")
requestPermissions(arrayOf(android.Manifest.permission.CAMERA), CAMERA_PERMISSION_REQUEST_CODE)
}
}
override fun onResume() {
super.onResume()
coreContext.core.nativePreviewWindowId = binding.qrCodeCaptureTexture
coreContext.core.isQrcodeVideoPreviewEnabled = true
coreContext.core.isVideoPreviewEnabled = true
}
override fun onPause() {
coreContext.core.nativePreviewWindowId = null
coreContext.core.isQrcodeVideoPreviewEnabled = false
coreContext.core.isVideoPreviewEnabled = false
super.onPause()
}
@Deprecated("Deprecated in Java")
override fun onRequestPermissionsResult(
requestCode: Int,
permissions: Array<out String>,
grantResults: IntArray
) {
if (requestCode == CAMERA_PERMISSION_REQUEST_CODE) {
val granted =
grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED
if (granted) {
Log.i("[QR Code] CAMERA permission granted")
coreContext.core.reloadVideoDevices()
viewModel.setBackCamera()
} else {
Log.w("[QR Code] CAMERA permission denied")
findNavController().navigateUp()
}
}
}
}

View file

@ -1,84 +0,0 @@
/*
* Copyright (c) 2010-2020 Belledonne Communications SARL.
*
* This file is part of linphone-android
* (see https://www.linphone.org).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.linphone.activities.assistant.fragments
import android.os.Bundle
import android.view.View
import androidx.lifecycle.ViewModelProvider
import org.linphone.LinphoneApplication.Companion.coreContext
import org.linphone.R
import org.linphone.activities.GenericFragment
import org.linphone.activities.assistant.AssistantActivity
import org.linphone.activities.assistant.viewmodels.RemoteProvisioningViewModel
import org.linphone.activities.assistant.viewmodels.SharedAssistantViewModel
import org.linphone.activities.navigateToEchoCancellerCalibration
import org.linphone.activities.navigateToQrCode
import org.linphone.databinding.AssistantRemoteProvisioningFragmentBinding
class RemoteProvisioningFragment : GenericFragment<AssistantRemoteProvisioningFragmentBinding>() {
private lateinit var sharedAssistantViewModel: SharedAssistantViewModel
private lateinit var viewModel: RemoteProvisioningViewModel
override fun getLayoutId(): Int = R.layout.assistant_remote_provisioning_fragment
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding.lifecycleOwner = viewLifecycleOwner
sharedAssistantViewModel = requireActivity().run {
ViewModelProvider(this)[SharedAssistantViewModel::class.java]
}
viewModel = ViewModelProvider(this)[RemoteProvisioningViewModel::class.java]
binding.viewModel = viewModel
binding.setQrCodeClickListener {
navigateToQrCode()
}
viewModel.fetchSuccessfulEvent.observe(
viewLifecycleOwner
) {
it.consume { success ->
if (success) {
if (coreContext.core.isEchoCancellerCalibrationRequired) {
navigateToEchoCancellerCalibration()
} else {
requireActivity().finish()
}
} else {
val activity = requireActivity() as AssistantActivity
activity.showSnackBar(R.string.assistant_remote_provisioning_failure)
}
}
}
viewModel.urlToFetch.value = sharedAssistantViewModel.remoteProvisioningUrl.value ?: coreContext.core.provisioningUri
}
override fun onDestroy() {
super.onDestroy()
if (::sharedAssistantViewModel.isInitialized) {
sharedAssistantViewModel.remoteProvisioningUrl.value = null
}
}
}

View file

@ -1,134 +0,0 @@
/*
* Copyright (c) 2010-2020 Belledonne Communications SARL.
*
* This file is part of linphone-android
* (see https://www.linphone.org).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.linphone.activities.assistant.fragments
import android.content.Intent
import android.net.Uri
import android.os.Bundle
import android.text.SpannableString
import android.text.Spanned
import android.text.method.LinkMovementMethod
import android.text.style.ClickableSpan
import android.view.View
import androidx.lifecycle.ViewModelProvider
import java.util.regex.Pattern
import org.linphone.LinphoneApplication.Companion.corePreferences
import org.linphone.R
import org.linphone.activities.*
import org.linphone.activities.assistant.viewmodels.WelcomeViewModel
import org.linphone.activities.navigateToAccountLogin
import org.linphone.activities.navigateToEmailAccountCreation
import org.linphone.activities.navigateToRemoteProvisioning
import org.linphone.core.tools.Log
import org.linphone.databinding.AssistantWelcomeFragmentBinding
class WelcomeFragment : GenericFragment<AssistantWelcomeFragmentBinding>() {
private lateinit var viewModel: WelcomeViewModel
override fun getLayoutId(): Int = R.layout.assistant_welcome_fragment
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding.lifecycleOwner = viewLifecycleOwner
viewModel = ViewModelProvider(this)[WelcomeViewModel::class.java]
binding.viewModel = viewModel
binding.setCreateAccountClickListener {
if (resources.getBoolean(R.bool.isTablet)) {
navigateToEmailAccountCreation()
} else {
navigateToPhoneAccountCreation()
}
}
binding.setAccountLoginClickListener {
navigateToAccountLogin()
}
binding.setGenericAccountLoginClickListener {
navigateToGenericLoginWarning()
}
binding.setRemoteProvisioningClickListener {
navigateToRemoteProvisioning()
}
viewModel.termsAndPrivacyAccepted.observe(
viewLifecycleOwner
) {
if (it) corePreferences.readAndAgreeTermsAndPrivacy = true
}
setUpTermsAndPrivacyLinks()
}
private fun setUpTermsAndPrivacyLinks() {
val terms = getString(R.string.assistant_general_terms)
val privacy = getString(R.string.assistant_privacy_policy)
val label = getString(
R.string.assistant_read_and_agree_terms,
terms,
privacy
)
val spannable = SpannableString(label)
val termsMatcher = Pattern.compile(terms).matcher(label)
if (termsMatcher.find()) {
val clickableSpan: ClickableSpan = object : ClickableSpan() {
override fun onClick(widget: View) {
val browserIntent = Intent(
Intent.ACTION_VIEW,
Uri.parse(getString(R.string.assistant_general_terms_link))
)
try {
startActivity(browserIntent)
} catch (e: Exception) {
Log.e("[Welcome] Can't start activity: $e")
}
}
}
spannable.setSpan(clickableSpan, termsMatcher.start(0), termsMatcher.end(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
}
val policyMatcher = Pattern.compile(privacy).matcher(label)
if (policyMatcher.find()) {
val clickableSpan: ClickableSpan = object : ClickableSpan() {
override fun onClick(widget: View) {
val browserIntent = Intent(
Intent.ACTION_VIEW,
Uri.parse(getString(R.string.assistant_privacy_policy_link))
)
try {
startActivity(browserIntent)
} catch (e: Exception) {
Log.e("[Welcome] Can't start activity: $e")
}
}
}
spannable.setSpan(clickableSpan, policyMatcher.start(0), policyMatcher.end(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
}
binding.termsAndPrivacy.text = spannable
binding.termsAndPrivacy.movementMethod = LinkMovementMethod.getInstance()
}
}

View file

@ -1,87 +0,0 @@
/*
* Copyright (c) 2010-2020 Belledonne Communications SARL.
*
* This file is part of linphone-android
* (see https://www.linphone.org).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.linphone.activities.assistant.viewmodels
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.Transformations
import androidx.lifecycle.ViewModel
import org.linphone.activities.assistant.fragments.CountryPickerFragment
import org.linphone.core.AccountCreator
import org.linphone.core.DialPlan
import org.linphone.core.tools.Log
import org.linphone.utils.PhoneNumberUtils
abstract class AbstractPhoneViewModel(val accountCreator: AccountCreator) :
ViewModel(),
CountryPickerFragment.CountryPickedListener {
val prefix = MutableLiveData<String>()
val phoneNumber = MutableLiveData<String>()
val phoneNumberError = MutableLiveData<String>()
val countryName: LiveData<String> = Transformations.switchMap(prefix) {
getCountryNameFromPrefix(it)
}
init {
prefix.value = "+"
}
override fun onCountryClicked(dialPlan: DialPlan) {
prefix.value = "+${dialPlan.countryCallingCode}"
}
fun isPhoneNumberOk(): Boolean {
return countryName.value.orEmpty().isNotEmpty() && phoneNumber.value.orEmpty().isNotEmpty() && phoneNumberError.value.orEmpty().isEmpty()
}
fun updateFromPhoneNumberAndOrDialPlan(number: String?, dialPlan: DialPlan?) {
val internationalPrefix = "+${dialPlan?.countryCallingCode}"
if (dialPlan != null) {
Log.i("[Assistant] Found prefix from dial plan: ${dialPlan.countryCallingCode}")
prefix.value = internationalPrefix
}
if (number != null) {
Log.i("[Assistant] Found phone number: $number")
phoneNumber.value = if (number.startsWith(internationalPrefix)) {
number.substring(internationalPrefix.length)
} else {
number
}
}
}
private fun getCountryNameFromPrefix(prefix: String?): MutableLiveData<String> {
val country = MutableLiveData<String>()
country.value = ""
if (prefix != null && prefix.isNotEmpty()) {
val countryCode = if (prefix.first() == '+') prefix.substring(1) else prefix
val dialPlan = PhoneNumberUtils.getDialPlanFromCountryCallingPrefix(countryCode)
Log.i("[Assistant] Found dial plan $dialPlan from country code: $countryCode")
country.value = dialPlan?.country
}
return country
}
}

View file

@ -1,244 +0,0 @@
/*
* Copyright (c) 2010-2020 Belledonne Communications SARL.
*
* This file is part of linphone-android
* (see https://www.linphone.org).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.linphone.activities.assistant.viewmodels
import androidx.lifecycle.*
import org.linphone.LinphoneApplication.Companion.coreContext
import org.linphone.R
import org.linphone.core.*
import org.linphone.core.tools.Log
import org.linphone.utils.Event
import org.linphone.utils.PhoneNumberUtils
class AccountLoginViewModelFactory(private val accountCreator: AccountCreator) :
ViewModelProvider.NewInstanceFactory() {
@Suppress("UNCHECKED_CAST")
override fun <T : ViewModel> create(modelClass: Class<T>): T {
return AccountLoginViewModel(accountCreator) as T
}
}
class AccountLoginViewModel(accountCreator: AccountCreator) : AbstractPhoneViewModel(accountCreator) {
val loginWithUsernamePassword = MutableLiveData<Boolean>()
val username = MutableLiveData<String>()
val usernameError = MutableLiveData<String>()
val password = MutableLiveData<String>()
val passwordError = MutableLiveData<String>()
val loginEnabled: MediatorLiveData<Boolean> = MediatorLiveData()
val waitForServerAnswer = MutableLiveData<Boolean>()
val displayName = MutableLiveData<String>()
val leaveAssistantEvent: MutableLiveData<Event<Boolean>> by lazy {
MutableLiveData<Event<Boolean>>()
}
val invalidCredentialsEvent: MutableLiveData<Event<Boolean>> by lazy {
MutableLiveData<Event<Boolean>>()
}
val goToSmsValidationEvent: MutableLiveData<Event<Boolean>> by lazy {
MutableLiveData<Event<Boolean>>()
}
val onErrorEvent: MutableLiveData<Event<String>> by lazy {
MutableLiveData<Event<String>>()
}
private val listener = object : AccountCreatorListenerStub() {
override fun onRecoverAccount(
creator: AccountCreator,
status: AccountCreator.Status,
response: String?
) {
Log.i("[Assistant] [Account Login] Recover account status is $status")
waitForServerAnswer.value = false
if (status == AccountCreator.Status.RequestOk) {
goToSmsValidationEvent.value = Event(true)
} else {
onErrorEvent.value = Event("Error: ${status.name}")
}
}
}
private var proxyConfigToCheck: ProxyConfig? = null
private val coreListener = object : CoreListenerStub() {
@Deprecated("Deprecated in Java")
override fun onRegistrationStateChanged(
core: Core,
cfg: ProxyConfig,
state: RegistrationState,
message: String
) {
if (cfg == proxyConfigToCheck) {
Log.i("[Assistant] [Account Login] Registration state is $state: $message")
if (state == RegistrationState.Ok) {
waitForServerAnswer.value = false
leaveAssistantEvent.value = Event(true)
core.removeListener(this)
} else if (state == RegistrationState.Failed) {
waitForServerAnswer.value = false
invalidCredentialsEvent.value = Event(true)
core.removeListener(this)
}
}
}
}
init {
accountCreator.addListener(listener)
loginWithUsernamePassword.value = coreContext.context.resources.getBoolean(R.bool.isTablet)
loginEnabled.value = false
loginEnabled.addSource(prefix) {
loginEnabled.value = isLoginButtonEnabled()
}
loginEnabled.addSource(phoneNumber) {
loginEnabled.value = isLoginButtonEnabled()
}
loginEnabled.addSource(username) {
loginEnabled.value = isLoginButtonEnabled()
}
loginEnabled.addSource(password) {
loginEnabled.value = isLoginButtonEnabled()
}
loginEnabled.addSource(loginWithUsernamePassword) {
loginEnabled.value = isLoginButtonEnabled()
}
loginEnabled.addSource(phoneNumberError) {
loginEnabled.value = isLoginButtonEnabled()
}
}
override fun onCleared() {
accountCreator.removeListener(listener)
super.onCleared()
}
fun removeInvalidProxyConfig() {
val cfg = proxyConfigToCheck
cfg ?: return
val authInfo = cfg.findAuthInfo()
if (authInfo != null) coreContext.core.removeAuthInfo(authInfo)
coreContext.core.removeProxyConfig(cfg)
proxyConfigToCheck = null
}
fun continueEvenIfInvalidCredentials() {
leaveAssistantEvent.value = Event(true)
}
fun login() {
accountCreator.displayName = displayName.value
if (loginWithUsernamePassword.value == true) {
val result = accountCreator.setUsername(username.value)
if (result != AccountCreator.UsernameStatus.Ok) {
Log.e("[Assistant] [Account Login] Error [${result.name}] setting the username: ${username.value}")
usernameError.value = result.name
return
}
Log.i("[Assistant] [Account Login] Username is ${accountCreator.username}")
val result2 = accountCreator.setPassword(password.value)
if (result2 != AccountCreator.PasswordStatus.Ok) {
Log.e("[Assistant] [Account Login] Error [${result2.name}] setting the password")
passwordError.value = result2.name
return
}
waitForServerAnswer.value = true
coreContext.core.addListener(coreListener)
if (!createProxyConfig()) {
waitForServerAnswer.value = false
coreContext.core.removeListener(coreListener)
onErrorEvent.value = Event("Error: Failed to create account object")
}
} else {
val result = AccountCreator.PhoneNumberStatus.fromInt(accountCreator.setPhoneNumber(phoneNumber.value, prefix.value))
if (result != AccountCreator.PhoneNumberStatus.Ok) {
Log.e("[Assistant] [Account Login] Error [$result] setting the phone number: ${phoneNumber.value} with prefix: ${prefix.value}")
phoneNumberError.value = result.name
return
}
Log.i("[Assistant] [Account Login] Phone number is ${accountCreator.phoneNumber}")
val result2 = accountCreator.setUsername(accountCreator.phoneNumber)
if (result2 != AccountCreator.UsernameStatus.Ok) {
Log.e("[Assistant] [Account Login] Error [${result2.name}] setting the username: ${accountCreator.phoneNumber}")
usernameError.value = result2.name
return
}
Log.i("[Assistant] [Account Login] Username is ${accountCreator.username}")
waitForServerAnswer.value = true
val status = accountCreator.recoverAccount()
Log.i("[Assistant] [Account Login] Recover account returned $status")
if (status != AccountCreator.Status.RequestOk) {
waitForServerAnswer.value = false
onErrorEvent.value = Event("Error: ${status.name}")
}
}
}
private fun isLoginButtonEnabled(): Boolean {
return if (loginWithUsernamePassword.value == true) {
username.value.orEmpty().isNotEmpty() && password.value.orEmpty().isNotEmpty()
} else {
isPhoneNumberOk()
}
}
private fun createProxyConfig(): Boolean {
val proxyConfig: ProxyConfig? = accountCreator.createProxyConfig()
proxyConfigToCheck = proxyConfig
if (proxyConfig == null) {
Log.e("[Assistant] [Account Login] Account creator couldn't create proxy config")
onErrorEvent.value = Event("Error: Failed to create account object")
return false
}
proxyConfig.isPushNotificationAllowed = true
if (proxyConfig.dialPrefix.isNullOrEmpty()) {
val dialPlan = PhoneNumberUtils.getDialPlanForCurrentCountry(coreContext.context)
if (dialPlan != null) {
Log.i("[Assistant] [Account Login] Found dial plan country ${dialPlan.country} with international prefix ${dialPlan.countryCallingCode}")
proxyConfig.edit()
proxyConfig.dialPrefix = dialPlan.countryCallingCode
proxyConfig.done()
} else {
Log.w("[Assistant] [Account Login] Failed to find dial plan")
}
}
Log.i("[Assistant] [Account Login] Proxy config created")
return true
}
}

View file

@ -1,68 +0,0 @@
/*
* Copyright (c) 2010-2020 Belledonne Communications SARL.
*
* This file is part of linphone-android
* (see https://www.linphone.org).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.linphone.activities.assistant.viewmodels
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import org.linphone.LinphoneApplication.Companion.coreContext
import org.linphone.core.Core
import org.linphone.core.CoreListenerStub
import org.linphone.core.EcCalibratorStatus
import org.linphone.core.tools.Log
import org.linphone.utils.Event
class EchoCancellerCalibrationViewModel : ViewModel() {
val echoCalibrationTerminated = MutableLiveData<Event<Boolean>>()
private val listener = object : CoreListenerStub() {
override fun onEcCalibrationResult(core: Core, status: EcCalibratorStatus, delayMs: Int) {
if (status == EcCalibratorStatus.InProgress) return
echoCancellerCalibrationFinished(status, delayMs)
}
}
init {
coreContext.core.addListener(listener)
}
fun startEchoCancellerCalibration() {
coreContext.core.startEchoCancellerCalibration()
}
fun echoCancellerCalibrationFinished(status: EcCalibratorStatus, delay: Int) {
coreContext.core.removeListener(listener)
when (status) {
EcCalibratorStatus.DoneNoEcho -> {
Log.i("[Echo Canceller Calibration] Done, no echo")
}
EcCalibratorStatus.Done -> {
Log.i("[Echo Canceller Calibration] Done, delay is ${delay}ms")
}
EcCalibratorStatus.Failed -> {
Log.w("[Echo Canceller Calibration] Failed")
}
EcCalibratorStatus.InProgress -> {
Log.i("[Echo Canceller Calibration] In progress")
}
}
echoCalibrationTerminated.value = Event(true)
}
}

View file

@ -1,173 +0,0 @@
/*
* Copyright (c) 2010-2020 Belledonne Communications SARL.
*
* This file is part of linphone-android
* (see https://www.linphone.org).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.linphone.activities.assistant.viewmodels
import androidx.lifecycle.MediatorLiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import org.linphone.R
import org.linphone.core.AccountCreator
import org.linphone.core.AccountCreatorListenerStub
import org.linphone.core.tools.Log
import org.linphone.utils.AppUtils
import org.linphone.utils.Event
class EmailAccountCreationViewModelFactory(private val accountCreator: AccountCreator) :
ViewModelProvider.NewInstanceFactory() {
@Suppress("UNCHECKED_CAST")
override fun <T : ViewModel> create(modelClass: Class<T>): T {
return EmailAccountCreationViewModel(accountCreator) as T
}
}
class EmailAccountCreationViewModel(val accountCreator: AccountCreator) : ViewModel() {
val username = MutableLiveData<String>()
val usernameError = MutableLiveData<String>()
val email = MutableLiveData<String>()
val emailError = MutableLiveData<String>()
val password = MutableLiveData<String>()
val passwordError = MutableLiveData<String>()
val passwordConfirmation = MutableLiveData<String>()
val passwordConfirmationError = MutableLiveData<String>()
val displayName = MutableLiveData<String>()
val createEnabled: MediatorLiveData<Boolean> = MediatorLiveData()
val waitForServerAnswer = MutableLiveData<Boolean>()
val goToEmailValidationEvent = MutableLiveData<Event<Boolean>>()
val onErrorEvent: MutableLiveData<Event<String>> by lazy {
MutableLiveData<Event<String>>()
}
private val listener = object : AccountCreatorListenerStub() {
override fun onIsAccountExist(
creator: AccountCreator,
status: AccountCreator.Status,
response: String?
) {
Log.i("[Assistant] [Account Creation] onIsAccountExist status is $status")
when (status) {
AccountCreator.Status.AccountExist, AccountCreator.Status.AccountExistWithAlias -> {
waitForServerAnswer.value = false
usernameError.value = AppUtils.getString(R.string.assistant_error_username_already_exists)
}
AccountCreator.Status.AccountNotExist -> {
val createAccountStatus = creator.createAccount()
if (createAccountStatus != AccountCreator.Status.RequestOk) {
waitForServerAnswer.value = false
onErrorEvent.value = Event("Error: ${status.name}")
}
}
else -> {
waitForServerAnswer.value = false
onErrorEvent.value = Event("Error: ${status.name}")
}
}
}
override fun onCreateAccount(
creator: AccountCreator,
status: AccountCreator.Status,
response: String?
) {
Log.i("[Account Creation] onCreateAccount status is $status")
waitForServerAnswer.value = false
when (status) {
AccountCreator.Status.AccountCreated -> {
goToEmailValidationEvent.value = Event(true)
}
else -> {
onErrorEvent.value = Event("Error: ${status.name}")
}
}
}
}
init {
accountCreator.addListener(listener)
createEnabled.value = false
createEnabled.addSource(username) {
createEnabled.value = isCreateButtonEnabled()
}
createEnabled.addSource(usernameError) {
createEnabled.value = isCreateButtonEnabled()
}
createEnabled.addSource(email) {
createEnabled.value = isCreateButtonEnabled()
}
createEnabled.addSource(emailError) {
createEnabled.value = isCreateButtonEnabled()
}
createEnabled.addSource(password) {
createEnabled.value = isCreateButtonEnabled()
}
createEnabled.addSource(passwordError) {
createEnabled.value = isCreateButtonEnabled()
}
createEnabled.addSource(passwordConfirmation) {
createEnabled.value = isCreateButtonEnabled()
}
createEnabled.addSource(passwordConfirmationError) {
createEnabled.value = isCreateButtonEnabled()
}
}
override fun onCleared() {
accountCreator.removeListener(listener)
super.onCleared()
}
fun create() {
accountCreator.username = username.value
accountCreator.password = password.value
accountCreator.email = email.value
accountCreator.displayName = displayName.value
waitForServerAnswer.value = true
val status = accountCreator.isAccountExist
Log.i("[Assistant] [Account Creation] Account exists returned $status")
if (status != AccountCreator.Status.RequestOk) {
waitForServerAnswer.value = false
onErrorEvent.value = Event("Error: ${status.name}")
}
}
private fun isCreateButtonEnabled(): Boolean {
return username.value.orEmpty().isNotEmpty() &&
email.value.orEmpty().isNotEmpty() &&
password.value.orEmpty().isNotEmpty() &&
passwordConfirmation.value.orEmpty().isNotEmpty() &&
password.value == passwordConfirmation.value &&
usernameError.value.orEmpty().isEmpty() &&
emailError.value.orEmpty().isEmpty() &&
passwordError.value.orEmpty().isEmpty() &&
passwordConfirmationError.value.orEmpty().isEmpty()
}
}

View file

@ -1,126 +0,0 @@
/*
* Copyright (c) 2010-2020 Belledonne Communications SARL.
*
* This file is part of linphone-android
* (see https://www.linphone.org).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.linphone.activities.assistant.viewmodels
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import org.linphone.LinphoneApplication
import org.linphone.core.AccountCreator
import org.linphone.core.AccountCreatorListenerStub
import org.linphone.core.ProxyConfig
import org.linphone.core.tools.Log
import org.linphone.utils.Event
import org.linphone.utils.PhoneNumberUtils
class EmailAccountValidationViewModelFactory(private val accountCreator: AccountCreator) :
ViewModelProvider.NewInstanceFactory() {
@Suppress("UNCHECKED_CAST")
override fun <T : ViewModel> create(modelClass: Class<T>): T {
return EmailAccountValidationViewModel(accountCreator) as T
}
}
class EmailAccountValidationViewModel(val accountCreator: AccountCreator) : ViewModel() {
val email = MutableLiveData<String>()
val waitForServerAnswer = MutableLiveData<Boolean>()
val leaveAssistantEvent = MutableLiveData<Event<Boolean>>()
val onErrorEvent: MutableLiveData<Event<String>> by lazy {
MutableLiveData<Event<String>>()
}
private val listener = object : AccountCreatorListenerStub() {
override fun onIsAccountActivated(
creator: AccountCreator,
status: AccountCreator.Status,
response: String?
) {
Log.i("[Account Validation] onIsAccountActivated status is $status")
waitForServerAnswer.value = false
when (status) {
AccountCreator.Status.AccountActivated -> {
if (createProxyConfig()) {
leaveAssistantEvent.value = Event(true)
} else {
onErrorEvent.value = Event("Error: ${status.name}")
}
}
AccountCreator.Status.AccountNotActivated -> {
onErrorEvent.value = Event("Error: ${status.name}")
}
else -> {
onErrorEvent.value = Event("Error: ${status.name}")
}
}
}
}
init {
accountCreator.addListener(listener)
email.value = accountCreator.email
}
override fun onCleared() {
accountCreator.removeListener(listener)
super.onCleared()
}
fun finish() {
waitForServerAnswer.value = true
val status = accountCreator.isAccountActivated
Log.i("[Assistant] [Account Validation] Account exists returned $status")
if (status != AccountCreator.Status.RequestOk) {
waitForServerAnswer.value = false
onErrorEvent.value = Event("Error: ${status.name}")
}
}
private fun createProxyConfig(): Boolean {
val proxyConfig: ProxyConfig? = accountCreator.createProxyConfig()
if (proxyConfig == null) {
Log.e("[Assistant] [Account Validation] Account creator couldn't create proxy config")
onErrorEvent.value = Event("Error: Failed to create account object")
return false
}
proxyConfig.isPushNotificationAllowed = true
if (proxyConfig.dialPrefix.isNullOrEmpty()) {
val dialPlan = PhoneNumberUtils.getDialPlanForCurrentCountry(LinphoneApplication.coreContext.context)
if (dialPlan != null) {
Log.i("[Assistant] [Account Validation] Found dial plan country ${dialPlan.country} with international prefix ${dialPlan.countryCallingCode}")
proxyConfig.edit()
proxyConfig.dialPrefix = dialPlan.countryCallingCode
proxyConfig.done()
} else {
Log.w("[Assistant] [Account Validation] Failed to find dial plan")
}
}
Log.i("[Assistant] [Account Validation] Proxy config created")
return true
}
}

View file

@ -1,149 +0,0 @@
/*
* Copyright (c) 2010-2020 Belledonne Communications SARL.
*
* This file is part of linphone-android
* (see https://www.linphone.org).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.linphone.activities.assistant.viewmodels
import androidx.lifecycle.MediatorLiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import org.linphone.LinphoneApplication.Companion.coreContext
import org.linphone.core.*
import org.linphone.core.tools.Log
import org.linphone.utils.Event
class GenericLoginViewModelFactory(private val accountCreator: AccountCreator) :
ViewModelProvider.NewInstanceFactory() {
@Suppress("UNCHECKED_CAST")
override fun <T : ViewModel> create(modelClass: Class<T>): T {
return GenericLoginViewModel(accountCreator) as T
}
}
class GenericLoginViewModel(private val accountCreator: AccountCreator) : ViewModel() {
val username = MutableLiveData<String>()
val password = MutableLiveData<String>()
val domain = MutableLiveData<String>()
val displayName = MutableLiveData<String>()
val transport = MutableLiveData<TransportType>()
val loginEnabled: MediatorLiveData<Boolean> = MediatorLiveData()
val waitForServerAnswer = MutableLiveData<Boolean>()
val leaveAssistantEvent = MutableLiveData<Event<Boolean>>()
val invalidCredentialsEvent: MutableLiveData<Event<Boolean>> by lazy {
MutableLiveData<Event<Boolean>>()
}
val onErrorEvent: MutableLiveData<Event<String>> by lazy {
MutableLiveData<Event<String>>()
}
private var proxyConfigToCheck: ProxyConfig? = null
private val coreListener = object : CoreListenerStub() {
@Deprecated("Deprecated in Java")
override fun onRegistrationStateChanged(
core: Core,
cfg: ProxyConfig,
state: RegistrationState,
message: String
) {
if (cfg == proxyConfigToCheck) {
Log.i("[Assistant] [Generic Login] Registration state is $state: $message")
if (state == RegistrationState.Ok) {
waitForServerAnswer.value = false
leaveAssistantEvent.value = Event(true)
core.removeListener(this)
} else if (state == RegistrationState.Failed) {
waitForServerAnswer.value = false
invalidCredentialsEvent.value = Event(true)
core.removeListener(this)
}
}
}
}
init {
transport.value = TransportType.Tls
loginEnabled.value = false
loginEnabled.addSource(username) {
loginEnabled.value = isLoginButtonEnabled()
}
loginEnabled.addSource(password) {
loginEnabled.value = isLoginButtonEnabled()
}
loginEnabled.addSource(domain) {
loginEnabled.value = isLoginButtonEnabled()
}
}
fun setTransport(transportType: TransportType) {
transport.value = transportType
}
fun removeInvalidProxyConfig() {
val cfg = proxyConfigToCheck
cfg ?: return
val authInfo = cfg.findAuthInfo()
if (authInfo != null) coreContext.core.removeAuthInfo(authInfo)
coreContext.core.removeProxyConfig(cfg)
proxyConfigToCheck = null
}
fun continueEvenIfInvalidCredentials() {
leaveAssistantEvent.value = Event(true)
}
fun createProxyConfig() {
waitForServerAnswer.value = true
coreContext.core.addListener(coreListener)
accountCreator.username = username.value
accountCreator.password = password.value
accountCreator.domain = domain.value
accountCreator.displayName = displayName.value
accountCreator.transport = transport.value
val proxyConfig: ProxyConfig? = accountCreator.createProxyConfig()
proxyConfigToCheck = proxyConfig
if (proxyConfig == null) {
Log.e("[Assistant] [Generic Login] Account creator couldn't create proxy config")
coreContext.core.removeListener(coreListener)
onErrorEvent.value = Event("Error: Failed to create account object")
waitForServerAnswer.value = false
return
}
Log.i("[Assistant] [Generic Login] Proxy config created")
}
private fun isLoginButtonEnabled(): Boolean {
return username.value.orEmpty().isNotEmpty() && domain.value.orEmpty().isNotEmpty() && password.value.orEmpty().isNotEmpty()
}
}

View file

@ -1,173 +0,0 @@
/*
* Copyright (c) 2010-2020 Belledonne Communications SARL.
*
* This file is part of linphone-android
* (see https://www.linphone.org).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.linphone.activities.assistant.viewmodels
import androidx.lifecycle.MediatorLiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import org.linphone.LinphoneApplication.Companion.corePreferences
import org.linphone.R
import org.linphone.core.AccountCreator
import org.linphone.core.AccountCreatorListenerStub
import org.linphone.core.tools.Log
import org.linphone.utils.AppUtils
import org.linphone.utils.Event
class PhoneAccountCreationViewModelFactory(private val accountCreator: AccountCreator) :
ViewModelProvider.NewInstanceFactory() {
@Suppress("UNCHECKED_CAST")
override fun <T : ViewModel> create(modelClass: Class<T>): T {
return PhoneAccountCreationViewModel(accountCreator) as T
}
}
class PhoneAccountCreationViewModel(accountCreator: AccountCreator) : AbstractPhoneViewModel(accountCreator) {
val username = MutableLiveData<String>()
val useUsername = MutableLiveData<Boolean>()
val usernameError = MutableLiveData<String>()
val displayName = MutableLiveData<String>()
val createEnabled: MediatorLiveData<Boolean> = MediatorLiveData()
val waitForServerAnswer = MutableLiveData<Boolean>()
val goToSmsValidationEvent: MutableLiveData<Event<Boolean>> by lazy {
MutableLiveData<Event<Boolean>>()
}
val onErrorEvent: MutableLiveData<Event<String>> by lazy {
MutableLiveData<Event<String>>()
}
private val listener = object : AccountCreatorListenerStub() {
override fun onIsAccountExist(
creator: AccountCreator,
status: AccountCreator.Status,
response: String?
) {
Log.i("[Phone Account Creation] onIsAccountExist status is $status")
when (status) {
AccountCreator.Status.AccountExist, AccountCreator.Status.AccountExistWithAlias -> {
waitForServerAnswer.value = false
if (useUsername.value == true) {
usernameError.value = AppUtils.getString(R.string.assistant_error_username_already_exists)
} else {
phoneNumberError.value = AppUtils.getString(R.string.assistant_error_phone_number_already_exists)
}
}
AccountCreator.Status.AccountNotExist -> {
val createAccountStatus = creator.createAccount()
Log.i("[Phone Account Creation] createAccount returned $createAccountStatus")
if (createAccountStatus != AccountCreator.Status.RequestOk) {
waitForServerAnswer.value = false
onErrorEvent.value = Event("Error: ${status.name}")
}
}
else -> {
waitForServerAnswer.value = false
onErrorEvent.value = Event("Error: ${status.name}")
}
}
}
override fun onCreateAccount(
creator: AccountCreator,
status: AccountCreator.Status,
response: String?
) {
Log.i("[Phone Account Creation] onCreateAccount status is $status")
waitForServerAnswer.value = false
when (status) {
AccountCreator.Status.AccountCreated -> {
goToSmsValidationEvent.value = Event(true)
}
AccountCreator.Status.AccountExistWithAlias -> {
phoneNumberError.value = AppUtils.getString(R.string.assistant_error_phone_number_already_exists)
}
else -> {
onErrorEvent.value = Event("Error: ${status.name}")
}
}
}
}
init {
useUsername.value = false
accountCreator.addListener(listener)
createEnabled.value = false
createEnabled.addSource(prefix) {
createEnabled.value = isCreateButtonEnabled()
}
createEnabled.addSource(phoneNumber) {
createEnabled.value = isCreateButtonEnabled()
}
createEnabled.addSource(useUsername) {
createEnabled.value = isCreateButtonEnabled()
}
createEnabled.addSource(username) {
createEnabled.value = isCreateButtonEnabled()
}
createEnabled.addSource(usernameError) {
createEnabled.value = isCreateButtonEnabled()
}
createEnabled.addSource(phoneNumberError) {
createEnabled.value = isCreateButtonEnabled()
}
}
override fun onCleared() {
accountCreator.removeListener(listener)
super.onCleared()
}
fun create() {
accountCreator.displayName = displayName.value
accountCreator.setPhoneNumber(phoneNumber.value, prefix.value)
if (useUsername.value == true) {
accountCreator.username = username.value
} else {
accountCreator.username = accountCreator.phoneNumber
}
waitForServerAnswer.value = true
val status = accountCreator.isAccountExist
Log.i("[Phone Account Creation] isAccountExist returned $status")
if (status != AccountCreator.Status.RequestOk) {
waitForServerAnswer.value = false
onErrorEvent.value = Event("Error: ${status.name}")
}
}
private fun isCreateButtonEnabled(): Boolean {
val usernameRegexp = corePreferences.config.getString("assistant", "username_regex", "^[a-z0-9+_.\\-]*\$")
return isPhoneNumberOk() && usernameRegexp != null &&
(
useUsername.value == false ||
username.value.orEmpty().matches(Regex(usernameRegexp)) &&
username.value.orEmpty().isNotEmpty() &&
usernameError.value.orEmpty().isEmpty()
)
}
}

View file

@ -1,142 +0,0 @@
/*
* Copyright (c) 2010-2020 Belledonne Communications SARL.
*
* This file is part of linphone-android
* (see https://www.linphone.org).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.linphone.activities.assistant.viewmodels
import androidx.lifecycle.*
import org.linphone.core.AccountCreator
import org.linphone.core.AccountCreatorListenerStub
import org.linphone.core.tools.Log
import org.linphone.utils.Event
class PhoneAccountLinkingViewModelFactory(private val accountCreator: AccountCreator) :
ViewModelProvider.NewInstanceFactory() {
@Suppress("UNCHECKED_CAST")
override fun <T : ViewModel> create(modelClass: Class<T>): T {
return PhoneAccountLinkingViewModel(accountCreator) as T
}
}
class PhoneAccountLinkingViewModel(accountCreator: AccountCreator) : AbstractPhoneViewModel(accountCreator) {
val username = MutableLiveData<String>()
val allowSkip = MutableLiveData<Boolean>()
val linkEnabled: MediatorLiveData<Boolean> = MediatorLiveData()
val waitForServerAnswer = MutableLiveData<Boolean>()
val leaveAssistantEvent = MutableLiveData<Event<Boolean>>()
val goToSmsValidationEvent = MutableLiveData<Event<Boolean>>()
val onErrorEvent: MutableLiveData<Event<String>> by lazy {
MutableLiveData<Event<String>>()
}
private val listener = object : AccountCreatorListenerStub() {
override fun onIsAliasUsed(
creator: AccountCreator,
status: AccountCreator.Status,
response: String?
) {
Log.i("[Phone Account Linking] onIsAliasUsed status is $status")
when (status) {
AccountCreator.Status.AliasNotExist -> {
if (creator.linkAccount() != AccountCreator.Status.RequestOk) {
Log.e("[Phone Account Linking] linkAccount status is $status")
waitForServerAnswer.value = false
onErrorEvent.value = Event("Error: ${status.name}")
}
}
AccountCreator.Status.AliasExist, AccountCreator.Status.AliasIsAccount -> {
waitForServerAnswer.value = false
onErrorEvent.value = Event("Error: ${status.name}")
}
else -> {
waitForServerAnswer.value = false
onErrorEvent.value = Event("Error: ${status.name}")
}
}
}
override fun onLinkAccount(
creator: AccountCreator,
status: AccountCreator.Status,
response: String?
) {
Log.i("[Phone Account Linking] onLinkAccount status is $status")
waitForServerAnswer.value = false
when (status) {
AccountCreator.Status.RequestOk -> {
goToSmsValidationEvent.value = Event(true)
}
else -> {
onErrorEvent.value = Event("Error: ${status.name}")
}
}
}
}
init {
accountCreator.addListener(listener)
linkEnabled.value = false
linkEnabled.addSource(prefix) {
linkEnabled.value = isLinkButtonEnabled()
}
linkEnabled.addSource(phoneNumber) {
linkEnabled.value = isLinkButtonEnabled()
}
linkEnabled.addSource(phoneNumberError) {
linkEnabled.value = isLinkButtonEnabled()
}
}
override fun onCleared() {
accountCreator.removeListener(listener)
super.onCleared()
}
fun link() {
accountCreator.setPhoneNumber(phoneNumber.value, prefix.value)
accountCreator.username = username.value
Log.i("[Assistant] [Phone Account Linking] Phone number is ${accountCreator.phoneNumber}")
waitForServerAnswer.value = true
val status: AccountCreator.Status = accountCreator.isAliasUsed
Log.i("[Phone Account Linking] isAliasUsed returned $status")
if (status != AccountCreator.Status.RequestOk) {
waitForServerAnswer.value = false
onErrorEvent.value = Event("Error: ${status.name}")
}
}
fun skip() {
leaveAssistantEvent.value = Event(true)
}
private fun isLinkButtonEnabled(): Boolean {
return isPhoneNumberOk()
}
}

View file

@ -1,156 +0,0 @@
/*
* Copyright (c) 2010-2020 Belledonne Communications SARL.
*
* This file is part of linphone-android
* (see https://www.linphone.org).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.linphone.activities.assistant.viewmodels
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import org.linphone.core.AccountCreator
import org.linphone.core.AccountCreatorListenerStub
import org.linphone.core.ProxyConfig
import org.linphone.core.tools.Log
import org.linphone.utils.Event
class PhoneAccountValidationViewModelFactory(private val accountCreator: AccountCreator) :
ViewModelProvider.NewInstanceFactory() {
@Suppress("UNCHECKED_CAST")
override fun <T : ViewModel> create(modelClass: Class<T>): T {
return PhoneAccountValidationViewModel(accountCreator) as T
}
}
class PhoneAccountValidationViewModel(val accountCreator: AccountCreator) : ViewModel() {
val phoneNumber = MutableLiveData<String>()
val code = MutableLiveData<String>()
val isLogin = MutableLiveData<Boolean>()
val isCreation = MutableLiveData<Boolean>()
val isLinking = MutableLiveData<Boolean>()
val waitForServerAnswer = MutableLiveData<Boolean>()
val leaveAssistantEvent = MutableLiveData<Event<Boolean>>()
val onErrorEvent: MutableLiveData<Event<String>> by lazy {
MutableLiveData<Event<String>>()
}
val listener = object : AccountCreatorListenerStub() {
override fun onLoginLinphoneAccount(
creator: AccountCreator,
status: AccountCreator.Status,
response: String?
) {
Log.i("[Assistant] [Phone Account Validation] onLoginLinphoneAccount status is $status")
waitForServerAnswer.value = false
if (status == AccountCreator.Status.RequestOk) {
if (createProxyConfig()) {
leaveAssistantEvent.value = Event(true)
} else {
onErrorEvent.value = Event("Error: Failed to create account object")
}
} else {
onErrorEvent.value = Event("Error: ${status.name}")
}
}
override fun onActivateAlias(
creator: AccountCreator,
status: AccountCreator.Status,
response: String?
) {
Log.i("[Assistant] [Phone Account Validation] onActivateAlias status is $status")
waitForServerAnswer.value = false
when (status) {
AccountCreator.Status.AccountActivated -> {
leaveAssistantEvent.value = Event(true)
}
else -> {
onErrorEvent.value = Event("Error: ${status.name}")
}
}
}
override fun onActivateAccount(
creator: AccountCreator,
status: AccountCreator.Status,
response: String?
) {
Log.i("[Assistant] [Phone Account Validation] onActivateAccount status is $status")
waitForServerAnswer.value = false
if (status == AccountCreator.Status.AccountActivated) {
if (createProxyConfig()) {
leaveAssistantEvent.value = Event(true)
} else {
onErrorEvent.value = Event("Error: Failed to create account object")
}
} else {
onErrorEvent.value = Event("Error: ${status.name}")
}
}
}
init {
accountCreator.addListener(listener)
}
override fun onCleared() {
accountCreator.removeListener(listener)
super.onCleared()
}
fun finish() {
accountCreator.activationCode = code.value.orEmpty()
Log.i("[Assistant] [Phone Account Validation] Phone number is ${accountCreator.phoneNumber} and activation code is ${accountCreator.activationCode}")
waitForServerAnswer.value = true
val status = when {
isLogin.value == true -> accountCreator.loginLinphoneAccount()
isCreation.value == true -> accountCreator.activateAccount()
isLinking.value == true -> accountCreator.activateAlias()
else -> AccountCreator.Status.UnexpectedError
}
Log.i("[Assistant] [Phone Account Validation] Code validation result is $status")
if (status != AccountCreator.Status.RequestOk) {
waitForServerAnswer.value = false
onErrorEvent.value = Event("Error: ${status.name}")
}
}
private fun createProxyConfig(): Boolean {
val proxyConfig: ProxyConfig? = accountCreator.createProxyConfig()
if (proxyConfig == null) {
Log.e("[Assistant] [Phone Account Validation] Account creator couldn't create proxy config")
return false
}
proxyConfig.isPushNotificationAllowed = true
Log.i("[Assistant] [Phone Account Validation] Proxy config created")
return true
}
}

View file

@ -1,73 +0,0 @@
/*
* Copyright (c) 2010-2020 Belledonne Communications SARL.
*
* This file is part of linphone-android
* (see https://www.linphone.org).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.linphone.activities.assistant.viewmodels
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import org.linphone.LinphoneApplication.Companion.coreContext
import org.linphone.core.Core
import org.linphone.core.CoreListenerStub
import org.linphone.core.tools.Log
import org.linphone.utils.Event
class QrCodeViewModel : ViewModel() {
val qrCodeFoundEvent = MutableLiveData<Event<String>>()
val showSwitchCamera = MutableLiveData<Boolean>()
private val listener = object : CoreListenerStub() {
override fun onQrcodeFound(core: Core, result: String?) {
Log.i("[QR Code] Found [$result]")
if (result != null) qrCodeFoundEvent.postValue(Event(result))
}
}
init {
coreContext.core.addListener(listener)
showSwitchCamera.value = coreContext.showSwitchCameraButton()
}
override fun onCleared() {
coreContext.core.removeListener(listener)
super.onCleared()
}
fun setBackCamera() {
showSwitchCamera.value = coreContext.showSwitchCameraButton()
for (camera in coreContext.core.videoDevicesList) {
if (camera.contains("Back")) {
Log.i("[QR Code] Found back facing camera: $camera")
coreContext.core.videoDevice = camera
return
}
}
val first = coreContext.core.videoDevicesList.firstOrNull()
if (first != null) {
Log.i("[QR Code] Using first camera found: $first")
coreContext.core.videoDevice = first
}
}
fun switchCamera() {
coreContext.switchCamera()
}
}

View file

@ -1,85 +0,0 @@
/*
* Copyright (c) 2010-2020 Belledonne Communications SARL.
*
* This file is part of linphone-android
* (see https://www.linphone.org).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.linphone.activities.assistant.viewmodels
import androidx.lifecycle.MediatorLiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import org.linphone.LinphoneApplication.Companion.coreContext
import org.linphone.core.ConfiguringState
import org.linphone.core.Core
import org.linphone.core.CoreListenerStub
import org.linphone.core.tools.Log
import org.linphone.utils.Event
class RemoteProvisioningViewModel : ViewModel() {
val urlToFetch = MutableLiveData<String>()
val urlError = MutableLiveData<String>()
val fetchEnabled: MediatorLiveData<Boolean> = MediatorLiveData()
val fetchInProgress = MutableLiveData<Boolean>()
val fetchSuccessfulEvent = MutableLiveData<Event<Boolean>>()
private val listener = object : CoreListenerStub() {
override fun onConfiguringStatus(core: Core, status: ConfiguringState, message: String?) {
fetchInProgress.value = false
when (status) {
ConfiguringState.Successful -> {
fetchSuccessfulEvent.value = Event(true)
}
ConfiguringState.Failed -> {
fetchSuccessfulEvent.value = Event(false)
}
else -> {}
}
}
}
init {
fetchInProgress.value = false
coreContext.core.addListener(listener)
fetchEnabled.value = false
fetchEnabled.addSource(urlToFetch) {
fetchEnabled.value = isFetchEnabled()
}
fetchEnabled.addSource(urlError) {
fetchEnabled.value = isFetchEnabled()
}
}
override fun onCleared() {
coreContext.core.removeListener(listener)
super.onCleared()
}
fun fetchAndApply() {
val url = urlToFetch.value.orEmpty()
coreContext.core.provisioningUri = url
Log.w("[Remote Provisioning] Url set to [$url], restarting Core")
fetchInProgress.value = true
coreContext.core.stop()
coreContext.core.start()
}
private fun isFetchEnabled(): Boolean {
return urlToFetch.value.orEmpty().isNotEmpty() && urlError.value.orEmpty().isEmpty()
}
}

View file

@ -1,59 +0,0 @@
/*
* Copyright (c) 2010-2020 Belledonne Communications SARL.
*
* This file is part of linphone-android
* (see https://www.linphone.org).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.linphone.activities.assistant.viewmodels
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import java.util.*
import org.linphone.LinphoneApplication.Companion.coreContext
import org.linphone.LinphoneApplication.Companion.corePreferences
import org.linphone.core.*
import org.linphone.core.tools.Log
class SharedAssistantViewModel : ViewModel() {
val remoteProvisioningUrl = MutableLiveData<String>()
private var accountCreator: AccountCreator
private var useGenericSipAccount: Boolean = false
init {
Log.i("[Assistant] Loading linphone default values")
coreContext.core.loadConfigFromXml(corePreferences.linphoneDefaultValuesPath)
accountCreator = coreContext.core.createAccountCreator(corePreferences.xmlRpcServerUrl)
accountCreator.language = Locale.getDefault().language
}
fun getAccountCreator(genericAccountCreator: Boolean = false): AccountCreator {
if (genericAccountCreator != useGenericSipAccount) {
accountCreator.reset()
accountCreator.language = Locale.getDefault().language
if (genericAccountCreator) {
Log.i("[Assistant] Loading default values")
coreContext.core.loadConfigFromXml(corePreferences.defaultValuesPath)
} else {
Log.i("[Assistant] Loading linphone default values")
coreContext.core.loadConfigFromXml(corePreferences.linphoneDefaultValuesPath)
}
useGenericSipAccount = genericAccountCreator
}
return accountCreator
}
}

View file

@ -1,212 +0,0 @@
/*
* Copyright (c) 2010-2021 Belledonne Communications SARL.
*
* This file is part of linphone-android
* (see https://www.linphone.org).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.linphone.activities.chat_bubble
import android.content.Intent
import android.os.Bundle
import android.widget.Toast
import androidx.databinding.DataBindingUtil
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import org.linphone.LinphoneApplication.Companion.coreContext
import org.linphone.R
import org.linphone.activities.GenericActivity
import org.linphone.activities.main.MainActivity
import org.linphone.activities.main.chat.adapters.ChatMessagesListAdapter
import org.linphone.activities.main.chat.viewmodels.*
import org.linphone.activities.main.viewmodels.ListTopBarViewModel
import org.linphone.core.ChatRoom
import org.linphone.core.ChatRoomListenerStub
import org.linphone.core.EventLog
import org.linphone.core.Factory
import org.linphone.core.tools.Log
import org.linphone.databinding.ChatBubbleActivityBinding
import org.linphone.utils.FileUtils
class ChatBubbleActivity : GenericActivity() {
private lateinit var binding: ChatBubbleActivityBinding
private lateinit var viewModel: ChatRoomViewModel
private lateinit var listViewModel: ChatMessagesListViewModel
private lateinit var chatSendingViewModel: ChatMessageSendingViewModel
private lateinit var adapter: ChatMessagesListAdapter
private val observer = object : RecyclerView.AdapterDataObserver() {
override fun onItemRangeInserted(positionStart: Int, itemCount: Int) {
if (positionStart == adapter.itemCount - itemCount) {
adapter.notifyItemChanged(positionStart - 1) // For grouping purposes
scrollToBottom()
}
}
}
private val listener = object : ChatRoomListenerStub() {
override fun onChatMessagesReceived(chatRoom: ChatRoom, eventLogs: Array<out EventLog>) {
chatRoom.markAsRead()
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = DataBindingUtil.setContentView(this, R.layout.chat_bubble_activity)
binding.lifecycleOwner = this
val localSipUri = intent.getStringExtra("LocalSipUri")
val remoteSipUri = intent.getStringExtra("RemoteSipUri")
var chatRoom: ChatRoom? = null
if (localSipUri != null && remoteSipUri != null) {
Log.i("[Chat Bubble] Found local [$localSipUri] & remote [$remoteSipUri] addresses in arguments")
val localAddress = Factory.instance().createAddress(localSipUri)
val remoteSipAddress = Factory.instance().createAddress(remoteSipUri)
chatRoom = coreContext.core.searchChatRoom(
null, localAddress, remoteSipAddress,
arrayOfNulls(
0
)
)
}
if (chatRoom == null) {
Log.e("[Chat Bubble] Chat room is null, aborting!")
finish()
return
}
viewModel = ViewModelProvider(
this,
ChatRoomViewModelFactory(chatRoom)
)[ChatRoomViewModel::class.java]
binding.viewModel = viewModel
listViewModel = ViewModelProvider(
this,
ChatMessagesListViewModelFactory(chatRoom)
)[ChatMessagesListViewModel::class.java]
chatSendingViewModel = ViewModelProvider(
this,
ChatMessageSendingViewModelFactory(chatRoom)
)[ChatMessageSendingViewModel::class.java]
binding.chatSendingViewModel = chatSendingViewModel
val listSelectionViewModel = ViewModelProvider(this)[ListTopBarViewModel::class.java]
adapter = ChatMessagesListAdapter(listSelectionViewModel, this)
// SubmitList is done on a background thread
// We need this adapter data observer to know when to scroll
binding.chatMessagesList.adapter = adapter
adapter.registerAdapterDataObserver(observer)
// Disable context menu on each message
adapter.disableAdvancedContextMenuOptions()
adapter.openContentEvent.observe(
this
) {
it.consume { content ->
if (content.isFileEncrypted) {
Toast.makeText(
this,
R.string.chat_bubble_cant_open_enrypted_file,
Toast.LENGTH_LONG
).show()
} else {
FileUtils.openFileInThirdPartyApp(this, content.filePath.orEmpty(), true)
}
}
}
val layoutManager = LinearLayoutManager(this)
layoutManager.stackFromEnd = true
binding.chatMessagesList.layoutManager = layoutManager
listViewModel.events.observe(
this
) { events ->
adapter.submitList(events)
}
chatSendingViewModel.textToSend.observe(
this
) {
chatSendingViewModel.onTextToSendChanged(it)
}
binding.setOpenAppClickListener {
coreContext.notificationsManager.currentlyDisplayedChatRoomAddress = null
coreContext.notificationsManager.changeDismissNotificationUponReadForChatRoom(viewModel.chatRoom, false)
val intent = Intent(this, MainActivity::class.java)
intent.putExtra("RemoteSipUri", remoteSipUri)
intent.putExtra("LocalSipUri", localSipUri)
intent.putExtra("Chat", true)
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK and Intent.FLAG_ACTIVITY_CLEAR_TASK)
startActivity(intent)
}
binding.setCloseBubbleClickListener {
coreContext.notificationsManager.dismissChatNotification(viewModel.chatRoom)
}
binding.setSendMessageClickListener {
chatSendingViewModel.sendMessage()
binding.message.text?.clear()
}
}
override fun onResume() {
super.onResume()
viewModel.chatRoom.addListener(listener)
// Workaround for the removed notification when a chat room is marked as read
coreContext.notificationsManager.changeDismissNotificationUponReadForChatRoom(viewModel.chatRoom, true)
viewModel.chatRoom.markAsRead()
val peerAddress = viewModel.chatRoom.peerAddress.asStringUriOnly()
coreContext.notificationsManager.currentlyDisplayedChatRoomAddress = peerAddress
coreContext.notificationsManager.resetChatNotificationCounterForSipUri(peerAddress)
lifecycleScope.launch {
// Without the delay the scroll to bottom doesn't happen...
delay(100)
scrollToBottom()
}
}
override fun onPause() {
viewModel.chatRoom.removeListener(listener)
coreContext.notificationsManager.currentlyDisplayedChatRoomAddress = null
coreContext.notificationsManager.changeDismissNotificationUponReadForChatRoom(viewModel.chatRoom, false)
super.onPause()
}
private fun scrollToBottom() {
if (adapter.itemCount > 0) {
binding.chatMessagesList.scrollToPosition(adapter.itemCount - 1)
}
}
}

View file

@ -1,529 +0,0 @@
/*
* Copyright (c) 2010-2020 Belledonne Communications SARL.
*
* This file is part of linphone-android
* (see https://www.linphone.org).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.linphone.activities.main
import android.content.ComponentCallbacks2
import android.content.Intent
import android.content.res.Configuration
import android.net.Uri
import android.os.Bundle
import android.os.Parcelable
import android.view.Gravity
import android.view.MotionEvent
import android.view.View
import androidx.annotation.StringRes
import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
import androidx.core.view.doOnAttach
import androidx.databinding.DataBindingUtil
import androidx.fragment.app.FragmentContainerView
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.lifecycleScope
import androidx.navigation.NavController
import androidx.navigation.NavDestination
import androidx.navigation.findNavController
import androidx.window.layout.FoldingFeature
import coil.imageLoader
import com.google.android.material.snackbar.Snackbar
import java.io.UnsupportedEncodingException
import java.net.URLDecoder
import kotlin.math.abs
import kotlinx.coroutines.*
import org.linphone.LinphoneApplication.Companion.coreContext
import org.linphone.LinphoneApplication.Companion.corePreferences
import org.linphone.R
import org.linphone.activities.*
import org.linphone.activities.assistant.AssistantActivity
import org.linphone.activities.main.viewmodels.CallOverlayViewModel
import org.linphone.activities.main.viewmodels.SharedMainViewModel
import org.linphone.activities.navigateToDialer
import org.linphone.compatibility.Compatibility
import org.linphone.contact.ContactsUpdatedListenerStub
import org.linphone.core.CorePreferences
import org.linphone.core.tools.Log
import org.linphone.databinding.MainActivityBinding
import org.linphone.utils.*
class MainActivity : GenericActivity(), SnackBarActivity, NavController.OnDestinationChangedListener {
private lateinit var binding: MainActivityBinding
private lateinit var sharedViewModel: SharedMainViewModel
private lateinit var callOverlayViewModel: CallOverlayViewModel
private val listener = object : ContactsUpdatedListenerStub() {
override fun onContactsUpdated() {
Log.i("[Main Activity] Contact(s) updated, update shortcuts")
if (corePreferences.contactsShortcuts) {
ShortcutsHelper.createShortcutsToContacts(this@MainActivity)
} else if (corePreferences.chatRoomShortcuts) {
ShortcutsHelper.createShortcutsToChatRooms(this@MainActivity)
}
}
}
private lateinit var tabsFragment: FragmentContainerView
private lateinit var statusFragment: FragmentContainerView
private var overlayX = 0f
private var overlayY = 0f
private var initPosX = 0f
private var initPosY = 0f
private var overlay: View? = null
private val componentCallbacks = object : ComponentCallbacks2 {
override fun onConfigurationChanged(newConfig: Configuration) { }
override fun onLowMemory() {
Log.w("[Main Activity] onLowMemory !")
}
override fun onTrimMemory(level: Int) {
Log.w("[Main Activity] onTrimMemory called with level $level !")
applicationContext.imageLoader.memoryCache?.clear()
}
}
override fun onLayoutChanges(foldingFeature: FoldingFeature?) {
sharedViewModel.layoutChangedEvent.value = Event(true)
}
private var shouldTabsBeVisibleDependingOnDestination = true
private var shouldTabsBeVisibleDueToOrientationAndKeyboard = true
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// Must be done before the setContentView
installSplashScreen()
binding = DataBindingUtil.setContentView(this, R.layout.main_activity)
binding.lifecycleOwner = this
sharedViewModel = ViewModelProvider(this)[SharedMainViewModel::class.java]
binding.viewModel = sharedViewModel
callOverlayViewModel = ViewModelProvider(this)[CallOverlayViewModel::class.java]
binding.callOverlayViewModel = callOverlayViewModel
sharedViewModel.toggleDrawerEvent.observe(
this
) {
it.consume {
if (binding.sideMenu.isDrawerOpen(Gravity.LEFT)) {
binding.sideMenu.closeDrawer(binding.sideMenuContent, true)
} else {
binding.sideMenu.openDrawer(binding.sideMenuContent, true)
}
}
}
coreContext.callErrorMessageResourceId.observe(
this
) {
it.consume { message ->
showSnackBar(message)
}
}
if (coreContext.core.accountList.isEmpty()) {
if (corePreferences.firstStart) {
startActivity(Intent(this, AssistantActivity::class.java))
}
}
tabsFragment = findViewById(R.id.tabs_fragment)
statusFragment = findViewById(R.id.status_fragment)
binding.root.doOnAttach {
Log.i("[Main Activity] Report UI has been fully drawn (TTFD)")
try {
reportFullyDrawn()
} catch (se: SecurityException) {
Log.e("[Main Activity] Security exception when doing reportFullyDrawn(): $se")
}
}
}
override fun onNewIntent(intent: Intent?) {
super.onNewIntent(intent)
if (intent != null) handleIntentParams(intent)
}
override fun onResume() {
super.onResume()
coreContext.contactsManager.addListener(listener)
}
override fun onPause() {
coreContext.contactsManager.removeListener(listener)
super.onPause()
}
override fun showSnackBar(@StringRes resourceId: Int) {
Snackbar.make(findViewById(R.id.coordinator), resourceId, Snackbar.LENGTH_LONG).show()
}
override fun showSnackBar(@StringRes resourceId: Int, action: Int, listener: () -> Unit) {
Snackbar
.make(findViewById(R.id.coordinator), resourceId, Snackbar.LENGTH_LONG)
.setAction(action) {
Log.i("[Snack Bar] Action listener triggered")
listener()
}
.show()
}
override fun showSnackBar(message: String) {
Snackbar.make(findViewById(R.id.coordinator), message, Snackbar.LENGTH_LONG).show()
}
override fun onPostCreate(savedInstanceState: Bundle?) {
super.onPostCreate(savedInstanceState)
registerComponentCallbacks(componentCallbacks)
findNavController(R.id.nav_host_fragment).addOnDestinationChangedListener(this)
binding.rootCoordinatorLayout.viewTreeObserver.addOnGlobalLayoutListener {
val portraitOrientation = resources.configuration.orientation != Configuration.ORIENTATION_LANDSCAPE
val keyboardVisible = ViewCompat.getRootWindowInsets(binding.rootCoordinatorLayout)
?.isVisible(WindowInsetsCompat.Type.ime()) == true
Log.d("[Tabs Fragment] Keyboard is ${if (keyboardVisible) "visible" else "invisible"}")
shouldTabsBeVisibleDueToOrientationAndKeyboard = !portraitOrientation || !keyboardVisible
updateTabsFragmentVisibility()
}
initOverlay()
if (intent != null) handleIntentParams(intent)
}
override fun onDestroy() {
findNavController(R.id.nav_host_fragment).removeOnDestinationChangedListener(this)
unregisterComponentCallbacks(componentCallbacks)
super.onDestroy()
}
override fun onDestinationChanged(
controller: NavController,
destination: NavDestination,
arguments: Bundle?
) {
hideKeyboard()
if (statusFragment.visibility == View.GONE) {
statusFragment.visibility = View.VISIBLE
}
shouldTabsBeVisibleDependingOnDestination = when (destination.id) {
R.id.masterCallLogsFragment, R.id.masterContactsFragment, R.id.dialerFragment, R.id.masterChatRoomsFragment ->
true
else -> false
}
updateTabsFragmentVisibility()
}
fun hideKeyboard() {
currentFocus?.hideKeyboard()
}
fun hideStatusFragment(hide: Boolean) {
statusFragment.visibility = if (hide) View.GONE else View.VISIBLE
}
private fun updateTabsFragmentVisibility() {
tabsFragment.visibility = if (shouldTabsBeVisibleDependingOnDestination && shouldTabsBeVisibleDueToOrientationAndKeyboard) View.VISIBLE else View.GONE
}
private fun handleIntentParams(intent: Intent) {
when (intent.action) {
Intent.ACTION_MAIN -> {
val core = coreContext.core
val call = core.currentCall ?: core.calls.firstOrNull()
if (call != null) {
Log.i("[Main Activity] Launcher clicked while there is at least one active call, go to CallActivity")
val callIntent = Intent(this, org.linphone.activities.voip.CallActivity::class.java)
callIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_REORDER_TO_FRONT)
startActivity(callIntent)
}
}
Intent.ACTION_SEND, Intent.ACTION_SENDTO -> {
if (intent.type == "text/plain") {
handleSendText(intent)
} else {
lifecycleScope.launch {
handleSendFile(intent)
}
}
}
Intent.ACTION_SEND_MULTIPLE -> {
lifecycleScope.launch {
handleSendMultipleFiles(intent)
}
}
Intent.ACTION_VIEW -> {
val uri = intent.data
if (intent.type == AppUtils.getString(R.string.linphone_address_mime_type)) {
if (uri != null) {
val contactId = coreContext.contactsManager.getAndroidContactIdFromUri(uri)
if (contactId != null) {
Log.i("[Main Activity] Found contact URI parameter in intent: $uri")
navigateToContact(contactId)
}
}
} else {
if (uri != null) {
handleTelOrSipUri(uri)
}
}
}
Intent.ACTION_DIAL, Intent.ACTION_CALL -> {
val uri = intent.data
if (uri != null) {
handleTelOrSipUri(uri)
}
}
Intent.ACTION_VIEW_LOCUS -> {
if (corePreferences.disableChat) return
val locus = Compatibility.extractLocusIdFromIntent(intent)
if (locus != null) {
Log.i("[Main Activity] Found chat room locus intent extra: $locus")
handleLocusOrShortcut(locus)
}
}
else -> {
when {
intent.hasExtra("ContactId") -> {
val id = intent.getStringExtra("ContactId")
Log.i("[Main Activity] Found contact ID in extras: $id")
navigateToContact(id)
}
intent.hasExtra("Chat") -> {
if (corePreferences.disableChat) return
if (intent.hasExtra("RemoteSipUri") && intent.hasExtra("LocalSipUri")) {
val peerAddress = intent.getStringExtra("RemoteSipUri")
val localAddress = intent.getStringExtra("LocalSipUri")
Log.i("[Main Activity] Found chat room intent extra: local SIP URI=[$localAddress], peer SIP URI=[$peerAddress]")
navigateToChatRoom(localAddress, peerAddress)
} else {
Log.i("[Main Activity] Found chat intent extra, go to chat rooms list")
navigateToChatRooms()
}
}
intent.hasExtra("Dialer") -> {
Log.i("[Main Activity] Found dialer intent extra, go to dialer")
val args = Bundle()
args.putBoolean("Transfer", intent.getBooleanExtra("Transfer", false))
navigateToDialer(args)
}
}
}
}
// Prevent this intent to be processed again
intent.action = null
intent.data = null
intent.extras?.clear()
}
private fun handleTelOrSipUri(uri: Uri) {
Log.i("[Main Activity] Found uri: $uri to call")
val stringUri = uri.toString()
var addressToCall: String = stringUri
when {
addressToCall.startsWith("tel:") -> {
Log.i("[Main Activity] Removing tel: prefix")
addressToCall = addressToCall.substring("tel:".length)
}
addressToCall.startsWith("linphone:") -> {
Log.i("[Main Activity] Removing linphone: prefix")
addressToCall = addressToCall.substring("linphone:".length)
}
addressToCall.startsWith("sip-linphone:") -> {
Log.i("[Main Activity] Removing linphone: sip-linphone")
addressToCall = addressToCall.substring("sip-linphone:".length)
}
}
val address = coreContext.core.interpretUrl(addressToCall, LinphoneUtils.applyInternationalPrefix())
if (address != null) {
addressToCall = address.asStringUriOnly()
}
Log.i("[Main Activity] Starting dialer with pre-filled URI $addressToCall")
val args = Bundle()
args.putString("URI", addressToCall)
navigateToDialer(args)
}
private fun handleSendText(intent: Intent) {
if (corePreferences.disableChat) return
intent.getStringExtra(Intent.EXTRA_TEXT)?.let {
sharedViewModel.textToShare.value = it
}
handleSendChatRoom(intent)
}
private suspend fun handleSendFile(intent: Intent) {
if (corePreferences.disableChat) return
Log.i("[Main Activity] Found single file to share with type ${intent.type}")
(intent.getParcelableExtra<Parcelable>(Intent.EXTRA_STREAM) as? Uri)?.let {
val list = arrayListOf<String>()
coroutineScope {
val deferred = async {
FileUtils.getFilePath(this@MainActivity, it)
}
val path = deferred.await()
if (path != null) {
list.add(path)
Log.i("[Main Activity] Found single file to share: $path")
}
}
sharedViewModel.filesToShare.value = list
}
// Check that the current fragment hasn't already handled the event on filesToShare
// If it has, don't go further.
// For example this may happen when picking a GIF from the keyboard while inside a chat room
if (!sharedViewModel.filesToShare.value.isNullOrEmpty()) {
handleSendChatRoom(intent)
}
}
private suspend fun handleSendMultipleFiles(intent: Intent) {
if (corePreferences.disableChat) return
intent.getParcelableArrayListExtra<Parcelable>(Intent.EXTRA_STREAM)?.let {
val list = arrayListOf<String>()
coroutineScope {
val deferred = arrayListOf<Deferred<String?>>()
for (parcelable in it) {
val uri = parcelable as Uri
deferred.add(async { FileUtils.getFilePath(this@MainActivity, uri) })
}
val paths = deferred.awaitAll()
for (path in paths) {
Log.i("[Main Activity] Found file to share: $path")
if (path != null) list.add(path)
}
}
sharedViewModel.filesToShare.value = list
}
handleSendChatRoom(intent)
}
private fun handleSendChatRoom(intent: Intent) {
if (corePreferences.disableChat) return
val uri = intent.data
if (uri != null) {
Log.i("[Main Activity] Found uri: $uri to send a message to")
val stringUri = uri.toString()
var addressToIM: String = stringUri
try {
addressToIM = URLDecoder.decode(stringUri, "UTF-8")
} catch (e: UnsupportedEncodingException) {
Log.e("[Main Activity] UnsupportedEncodingException: $e")
}
when {
addressToIM.startsWith("sms:") ->
addressToIM = addressToIM.substring("sms:".length)
addressToIM.startsWith("smsto:") ->
addressToIM = addressToIM.substring("smsto:".length)
addressToIM.startsWith("mms:") ->
addressToIM = addressToIM.substring("mms:".length)
addressToIM.startsWith("mmsto:") ->
addressToIM = addressToIM.substring("mmsto:".length)
}
val localAddress =
coreContext.core.defaultAccount?.params?.identityAddress?.asStringUriOnly()
val peerAddress = coreContext.core.interpretUrl(addressToIM, LinphoneUtils.applyInternationalPrefix())?.asStringUriOnly()
Log.i("[Main Activity] Navigating to chat room with local [$localAddress] and peer [$peerAddress] addresses")
navigateToChatRoom(localAddress, peerAddress)
} else {
val shortcutId = intent.getStringExtra("android.intent.extra.shortcut.ID") // Intent.EXTRA_SHORTCUT_ID
if (shortcutId != null) {
Log.i("[Main Activity] Found shortcut ID: $shortcutId")
handleLocusOrShortcut(shortcutId)
} else {
Log.i("[Main Activity] Going into chat rooms list")
navigateToChatRooms()
}
}
}
private fun handleLocusOrShortcut(id: String) {
val split = id.split("~")
if (split.size == 2) {
val localAddress = split[0]
val peerAddress = split[1]
Log.i("[Main Activity] Navigating to chat room with local [$localAddress] and peer [$peerAddress] addresses, computed from shortcut/locus id")
navigateToChatRoom(localAddress, peerAddress)
} else {
Log.e("[Main Activity] Failed to parse shortcut/locus id: $id, going to chat rooms list")
navigateToChatRooms()
}
}
private fun initOverlay() {
overlay = binding.root.findViewById(R.id.call_overlay)
val callOverlay = overlay
callOverlay ?: return
callOverlay.setOnTouchListener { view, event ->
when (event.action) {
MotionEvent.ACTION_DOWN -> {
overlayX = view.x - event.rawX
overlayY = view.y - event.rawY
initPosX = view.x
initPosY = view.y
}
MotionEvent.ACTION_MOVE -> {
view.animate()
.x(event.rawX + overlayX)
.y(event.rawY + overlayY)
.setDuration(0)
.start()
}
MotionEvent.ACTION_UP -> {
if (abs(initPosX - view.x) < CorePreferences.OVERLAY_CLICK_SENSITIVITY &&
abs(initPosY - view.y) < CorePreferences.OVERLAY_CLICK_SENSITIVITY
) {
view.performClick()
}
}
else -> return@setOnTouchListener false
}
true
}
callOverlay.setOnClickListener {
coreContext.onCallOverlayClick()
}
}
}

View file

@ -1,81 +0,0 @@
/*
* Copyright (c) 2010-2020 Belledonne Communications SARL.
*
* This file is part of linphone-android
* (see https://www.linphone.org).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.linphone.activities.main.about
import android.content.*
import android.net.Uri
import android.os.Bundle
import android.view.View
import androidx.lifecycle.ViewModelProvider
import org.linphone.R
import org.linphone.activities.main.fragments.SecureFragment
import org.linphone.core.tools.Log
import org.linphone.databinding.AboutFragmentBinding
class AboutFragment : SecureFragment<AboutFragmentBinding>() {
private lateinit var viewModel: AboutViewModel
override fun getLayoutId(): Int = R.layout.about_fragment
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding.lifecycleOwner = viewLifecycleOwner
viewModel = ViewModelProvider(this)[AboutViewModel::class.java]
binding.viewModel = viewModel
binding.setPrivacyPolicyClickListener {
val browserIntent = Intent(
Intent.ACTION_VIEW,
Uri.parse(getString(R.string.about_privacy_policy_link))
)
try {
startActivity(browserIntent)
} catch (se: SecurityException) {
Log.e("[About] Failed to start browser intent, $se")
}
}
binding.setLicenseClickListener {
val browserIntent = Intent(
Intent.ACTION_VIEW,
Uri.parse(getString(R.string.about_license_link))
)
try {
startActivity(browserIntent)
} catch (se: SecurityException) {
Log.e("[About] Failed to start browser intent, $se")
}
}
binding.setWeblateClickListener {
val browserIntent = Intent(
Intent.ACTION_VIEW,
Uri.parse(getString(R.string.about_weblate_link))
)
try {
startActivity(browserIntent)
} catch (se: SecurityException) {
Log.e("[About] Failed to start browser intent, $se")
}
}
}
}

View file

@ -1,29 +0,0 @@
/*
* Copyright (c) 2010-2020 Belledonne Communications SARL.
*
* This file is part of linphone-android
* (see https://www.linphone.org).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.linphone.activities.main.about
import androidx.lifecycle.ViewModel
import org.linphone.LinphoneApplication.Companion.coreContext
class AboutViewModel : ViewModel() {
val appVersion: String = coreContext.appVersion
val sdkVersion: String = coreContext.sdkVersion
}

View file

@ -1,40 +0,0 @@
/*
* Copyright (c) 2010-2020 Belledonne Communications SARL.
*
* This file is part of linphone-android
* (see https://www.linphone.org).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.linphone.activities.main.adapters
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.ListAdapter
import androidx.recyclerview.widget.RecyclerView
import org.linphone.activities.main.viewmodels.ListTopBarViewModel
abstract class SelectionListAdapter<T, VH : RecyclerView.ViewHolder>(
selectionVM: ListTopBarViewModel,
diff: DiffUtil.ItemCallback<T>
) :
ListAdapter<T, VH>(diff) {
private var _selectionViewModel: ListTopBarViewModel? = selectionVM
protected val selectionViewModel get() = _selectionViewModel!!
override fun onDetachedFromRecyclerView(recyclerView: RecyclerView) {
super.onDetachedFromRecyclerView(recyclerView)
_selectionViewModel = null
}
}

View file

@ -1,519 +0,0 @@
/*
* Copyright (c) 2010-2020 Belledonne Communications SARL.
*
* This file is part of linphone-android
* (see https://www.linphone.org).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.linphone.activities.main.chat.adapters
import android.content.ClipData
import android.content.ClipboardManager
import android.content.Context
import android.view.Gravity
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.PopupWindow
import androidx.databinding.DataBindingUtil
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.MutableLiveData
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.RecyclerView
import org.linphone.LinphoneApplication.Companion.coreContext
import org.linphone.R
import org.linphone.activities.main.adapters.SelectionListAdapter
import org.linphone.activities.main.chat.data.ChatMessageData
import org.linphone.activities.main.chat.data.EventData
import org.linphone.activities.main.chat.data.EventLogData
import org.linphone.activities.main.chat.data.OnContentClickedListener
import org.linphone.activities.main.viewmodels.ListTopBarViewModel
import org.linphone.core.*
import org.linphone.core.tools.Log
import org.linphone.databinding.ChatEventListCellBinding
import org.linphone.databinding.ChatMessageListCellBinding
import org.linphone.databinding.ChatMessageLongPressMenuBindingImpl
import org.linphone.databinding.ChatUnreadMessagesListHeaderBinding
import org.linphone.utils.AppUtils
import org.linphone.utils.Event
import org.linphone.utils.HeaderAdapter
class ChatMessagesListAdapter(
selectionVM: ListTopBarViewModel,
private val viewLifecycleOwner: LifecycleOwner
) : SelectionListAdapter<EventLogData, RecyclerView.ViewHolder>(selectionVM, ChatMessageDiffCallback()),
HeaderAdapter {
companion object {
const val MAX_TIME_TO_GROUP_MESSAGES = 60 // 1 minute
}
val resendMessageEvent: MutableLiveData<Event<ChatMessage>> by lazy {
MutableLiveData<Event<ChatMessage>>()
}
val deleteMessageEvent: MutableLiveData<Event<ChatMessage>> by lazy {
MutableLiveData<Event<ChatMessage>>()
}
val forwardMessageEvent: MutableLiveData<Event<ChatMessage>> by lazy {
MutableLiveData<Event<ChatMessage>>()
}
val replyMessageEvent: MutableLiveData<Event<ChatMessage>> by lazy {
MutableLiveData<Event<ChatMessage>>()
}
val showImdnForMessageEvent: MutableLiveData<Event<ChatMessage>> by lazy {
MutableLiveData<Event<ChatMessage>>()
}
val addSipUriToContactEvent: MutableLiveData<Event<String>> by lazy {
MutableLiveData<Event<String>>()
}
val openContentEvent: MutableLiveData<Event<Content>> by lazy {
MutableLiveData<Event<Content>>()
}
val urlClickEvent: MutableLiveData<Event<String>> by lazy {
MutableLiveData<Event<String>>()
}
val sipUriClickedEvent: MutableLiveData<Event<String>> by lazy {
MutableLiveData<Event<String>>()
}
val callConferenceEvent: MutableLiveData<Event<Pair<String, String?>>> by lazy {
MutableLiveData<Event<Pair<String, String?>>>()
}
val scrollToChatMessageEvent: MutableLiveData<Event<ChatMessage>> by lazy {
MutableLiveData<Event<ChatMessage>>()
}
val errorEvent: MutableLiveData<Event<Int>> by lazy {
MutableLiveData<Event<Int>>()
}
private val contentClickedListener = object : OnContentClickedListener {
override fun onContentClicked(content: Content) {
openContentEvent.value = Event(content)
}
override fun onWebUrlClicked(url: String) {
if (popup?.isShowing == true) {
Log.w("[Chat Message Data] Long press that displayed context menu detected, aborting click on URL [$url]")
return
}
urlClickEvent.value = Event(url)
}
override fun onSipAddressClicked(sipUri: String) {
if (popup?.isShowing == true) {
Log.w("[Chat Message Data] Long press that displayed context menu detected, aborting click on SIP URI [$sipUri]")
return
}
sipUriClickedEvent.value = Event(sipUri)
}
override fun onCallConference(address: String, subject: String?) {
callConferenceEvent.value = Event(Pair(address, subject))
}
override fun onError(messageId: Int) {
errorEvent.value = Event(messageId)
}
}
private var advancedContextMenuOptionsDisabled: Boolean = false
private var popup: PopupWindow? = null
private var unreadMessagesCount: Int = 0
private var firstUnreadMessagePosition: Int = -1
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
return when (viewType) {
EventLog.Type.ConferenceChatMessage.toInt() -> createChatMessageViewHolder(parent)
else -> createEventViewHolder(parent)
}
}
private fun createChatMessageViewHolder(parent: ViewGroup): ChatMessageViewHolder {
val binding: ChatMessageListCellBinding = DataBindingUtil.inflate(
LayoutInflater.from(parent.context),
R.layout.chat_message_list_cell, parent, false
)
return ChatMessageViewHolder(binding)
}
private fun createEventViewHolder(parent: ViewGroup): EventViewHolder {
val binding: ChatEventListCellBinding = DataBindingUtil.inflate(
LayoutInflater.from(parent.context),
R.layout.chat_event_list_cell, parent, false
)
return EventViewHolder(binding)
}
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
val eventLog = getItem(position)
when (holder) {
is ChatMessageViewHolder -> holder.bind(eventLog)
is EventViewHolder -> holder.bind(eventLog)
}
}
override fun getItemViewType(position: Int): Int {
val eventLog = getItem(position)
return eventLog.eventLog.type.toInt()
}
override fun onCurrentListChanged(
previousList: MutableList<EventLogData>,
currentList: MutableList<EventLogData>
) {
// Need to wait for messages to be added before computing new first unread message position
firstUnreadMessagePosition = -1
}
override fun displayHeaderForPosition(position: Int): Boolean {
if (unreadMessagesCount > 0 && firstUnreadMessagePosition == -1) {
computeFirstUnreadMessagePosition()
}
return position == firstUnreadMessagePosition
}
override fun getHeaderViewForPosition(context: Context, position: Int): View {
val binding: ChatUnreadMessagesListHeaderBinding = DataBindingUtil.inflate(
LayoutInflater.from(context),
R.layout.chat_unread_messages_list_header, null, false
)
binding.title = AppUtils.getStringWithPlural(R.plurals.chat_room_unread_messages_event, unreadMessagesCount)
binding.executePendingBindings()
return binding.root
}
fun disableAdvancedContextMenuOptions() {
advancedContextMenuOptionsDisabled = true
}
fun setUnreadMessageCount(count: Int, forceUpdate: Boolean) {
// Once list has been filled once, don't show the unread message header
// when new messages are added to the history whilst it is visible
unreadMessagesCount = if (itemCount == 0 || forceUpdate) count else 0
firstUnreadMessagePosition = -1
}
fun getFirstUnreadMessagePosition(): Int {
return firstUnreadMessagePosition
}
private fun computeFirstUnreadMessagePosition() {
if (unreadMessagesCount > 0) {
var messageCount = 0
for (position in itemCount - 1 downTo 0) {
val eventLog = getItem(position)
val data = eventLog.data
if (data is ChatMessageData) {
messageCount += 1
if (messageCount == unreadMessagesCount) {
firstUnreadMessagePosition = position
break
}
}
}
}
}
inner class ChatMessageViewHolder(
val binding: ChatMessageListCellBinding
) : RecyclerView.ViewHolder(binding.root) {
fun bind(eventLog: EventLogData) {
with(binding) {
if (eventLog.eventLog.type == EventLog.Type.ConferenceChatMessage) {
val chatMessageViewModel = eventLog.data as ChatMessageData
chatMessageViewModel.setContentClickListener(contentClickedListener)
val chatMessage = chatMessageViewModel.chatMessage
data = chatMessageViewModel
lifecycleOwner = viewLifecycleOwner
// This is for item selection through ListTopBarFragment
selectionListViewModel = selectionViewModel
selectionViewModel.isEditionEnabled.observe(
viewLifecycleOwner
) {
position = bindingAdapterPosition
}
setClickListener {
if (selectionViewModel.isEditionEnabled.value == true) {
selectionViewModel.onToggleSelect(bindingAdapterPosition)
}
}
setReplyClickListener {
val reply = chatMessageViewModel.replyData.value?.chatMessage
if (reply != null) {
scrollToChatMessageEvent.value = Event(reply)
}
}
// Grouping
var hasPrevious = false
var hasNext = false
if (bindingAdapterPosition > 0) {
val previousItem = getItem(bindingAdapterPosition - 1)
if (previousItem.eventLog.type == EventLog.Type.ConferenceChatMessage) {
val previousMessage = previousItem.eventLog.chatMessage
if (previousMessage != null && previousMessage.fromAddress.weakEqual(chatMessage.fromAddress)) {
if (chatMessage.time - previousMessage.time < MAX_TIME_TO_GROUP_MESSAGES) {
hasPrevious = true
}
}
}
}
if (bindingAdapterPosition >= 0 && bindingAdapterPosition < itemCount - 1) {
val nextItem = getItem(bindingAdapterPosition + 1)
if (nextItem.eventLog.type == EventLog.Type.ConferenceChatMessage) {
val nextMessage = nextItem.eventLog.chatMessage
if (nextMessage != null && nextMessage.fromAddress.weakEqual(chatMessage.fromAddress)) {
if (nextMessage.time - chatMessage.time < MAX_TIME_TO_GROUP_MESSAGES) {
hasNext = true
}
}
}
}
chatMessageViewModel.updateBubbleBackground(hasPrevious, hasNext)
executePendingBindings()
setContextMenuClickListener {
val popupView: ChatMessageLongPressMenuBindingImpl = DataBindingUtil.inflate(
LayoutInflater.from(root.context),
R.layout.chat_message_long_press_menu, null, false
)
val itemSize = AppUtils.getDimension(R.dimen.chat_message_popup_item_height).toInt()
var totalSize = itemSize * 7
if (chatMessage.chatRoom.hasCapability(ChatRoomCapabilities.OneToOne.toInt())) {
// No message id
popupView.imdnHidden = true
totalSize -= itemSize
}
if (chatMessage.state != ChatMessage.State.NotDelivered) {
popupView.resendHidden = true
totalSize -= itemSize
}
if (chatMessage.contents.find { content -> content.isText } == null) {
popupView.copyTextHidden = true
totalSize -= itemSize
}
if (chatMessage.isOutgoing ||
chatMessageViewModel.contact.value != null ||
advancedContextMenuOptionsDisabled
) {
popupView.addToContactsHidden = true
totalSize -= itemSize
}
if (chatMessage.chatRoom.isReadOnly) {
popupView.replyHidden = true
totalSize -= itemSize
}
if (advancedContextMenuOptionsDisabled) {
popupView.forwardHidden = true
totalSize -= itemSize
}
// When using WRAP_CONTENT instead of real size, fails to place the
// popup window above if not enough space is available below
val popupWindow = PopupWindow(
popupView.root,
AppUtils.getDimension(R.dimen.chat_message_popup_width).toInt(),
totalSize,
true
)
popup = popupWindow
// Elevation is for showing a shadow around the popup
popupWindow.elevation = 20f
popupView.setResendClickListener {
resendMessage()
popupWindow.dismiss()
}
popupView.setCopyTextClickListener {
copyTextToClipboard()
popupWindow.dismiss()
}
popupView.setForwardClickListener {
forwardMessage()
popupWindow.dismiss()
}
popupView.setReplyClickListener {
replyMessage()
popupWindow.dismiss()
}
popupView.setImdnClickListener {
showImdnDeliveryFragment()
popupWindow.dismiss()
}
popupView.setAddToContactsClickListener {
addSenderToContacts()
popupWindow.dismiss()
}
popupView.setDeleteClickListener {
deleteMessage()
popupWindow.dismiss()
}
val gravity = if (chatMessage.isOutgoing) Gravity.END else Gravity.START
popupWindow.showAsDropDown(background, 0, 0, gravity or Gravity.TOP)
true
}
}
}
}
private fun resendMessage() {
val chatMessage = binding.data?.chatMessage
if (chatMessage != null) {
chatMessage.userData = bindingAdapterPosition
resendMessageEvent.value = Event(chatMessage)
}
}
private fun copyTextToClipboard() {
val chatMessage = binding.data?.chatMessage
if (chatMessage != null) {
val content = chatMessage.contents.find { content -> content.isText }
if (content != null) {
val clipboard: ClipboardManager =
coreContext.context.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
val clip = ClipData.newPlainText("Message", content.utf8Text)
clipboard.setPrimaryClip(clip)
}
}
}
private fun forwardMessage() {
val chatMessage = binding.data?.chatMessage
if (chatMessage != null) {
forwardMessageEvent.value = Event(chatMessage)
}
}
private fun replyMessage() {
val chatMessage = binding.data?.chatMessage
if (chatMessage != null) {
replyMessageEvent.value = Event(chatMessage)
}
}
private fun showImdnDeliveryFragment() {
val chatMessage = binding.data?.chatMessage
if (chatMessage != null) {
showImdnForMessageEvent.value = Event(chatMessage)
}
}
private fun deleteMessage() {
val chatMessage = binding.data?.chatMessage
if (chatMessage != null) {
chatMessage.userData = bindingAdapterPosition
deleteMessageEvent.value = Event(chatMessage)
}
}
private fun addSenderToContacts() {
val chatMessage = binding.data?.chatMessage
if (chatMessage != null) {
val copy = chatMessage.fromAddress.clone()
copy.clean() // To remove gruu if any
addSipUriToContactEvent.value = Event(copy.asStringUriOnly())
}
}
}
inner class EventViewHolder(
private val binding: ChatEventListCellBinding
) : RecyclerView.ViewHolder(binding.root) {
fun bind(eventLog: EventLogData) {
with(binding) {
val eventViewModel = eventLog.data as EventData
data = eventViewModel
binding.lifecycleOwner = viewLifecycleOwner
// This is for item selection through ListTopBarFragment
selectionListViewModel = selectionViewModel
selectionViewModel.isEditionEnabled.observe(
viewLifecycleOwner
) {
position = bindingAdapterPosition
}
binding.setClickListener {
if (selectionViewModel.isEditionEnabled.value == true) {
selectionViewModel.onToggleSelect(bindingAdapterPosition)
}
}
executePendingBindings()
}
}
}
}
private class ChatMessageDiffCallback : DiffUtil.ItemCallback<EventLogData>() {
override fun areItemsTheSame(
oldItem: EventLogData,
newItem: EventLogData
): Boolean {
return if (oldItem.type == EventLog.Type.ConferenceChatMessage &&
newItem.type == EventLog.Type.ConferenceChatMessage
) {
val oldData = (oldItem.data as ChatMessageData)
val newData = (newItem.data as ChatMessageData)
oldData.time.value == newData.time.value &&
oldData.isOutgoing == newData.isOutgoing
} else oldItem.notifyId == newItem.notifyId
}
override fun areContentsTheSame(
oldItem: EventLogData,
newItem: EventLogData
): Boolean {
return if (oldItem.type == EventLog.Type.ConferenceChatMessage &&
newItem.type == EventLog.Type.ConferenceChatMessage
) {
val oldData = (oldItem.data as ChatMessageData)
val newData = (newItem.data as ChatMessageData)
val previous = oldData.hasPreviousMessage == newData.hasPreviousMessage
val next = oldData.hasNextMessage == newData.hasNextMessage
val isDisplayed = newData.isDisplayed.value == true
isDisplayed && previous && next
} else {
oldItem.type != EventLog.Type.ConferenceChatMessage &&
newItem.type != EventLog.Type.ConferenceChatMessage
}
}
}

View file

@ -1,120 +0,0 @@
/*
* Copyright (c) 2010-2020 Belledonne Communications SARL.
*
* This file is part of linphone-android
* (see https://www.linphone.org).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.linphone.activities.main.chat.adapters
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.databinding.DataBindingUtil
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.MutableLiveData
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.RecyclerView
import org.linphone.R
import org.linphone.activities.main.adapters.SelectionListAdapter
import org.linphone.activities.main.chat.data.ChatRoomData
import org.linphone.activities.main.viewmodels.ListTopBarViewModel
import org.linphone.core.ChatRoom
import org.linphone.databinding.ChatRoomListCellBinding
import org.linphone.utils.Event
class ChatRoomsListAdapter(
selectionVM: ListTopBarViewModel,
private val viewLifecycleOwner: LifecycleOwner
) : SelectionListAdapter<ChatRoom, RecyclerView.ViewHolder>(selectionVM, ChatRoomDiffCallback()) {
val selectedChatRoomEvent: MutableLiveData<Event<ChatRoom>> by lazy {
MutableLiveData<Event<ChatRoom>>()
}
private var isForwardPending = false
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val binding: ChatRoomListCellBinding = DataBindingUtil.inflate(
LayoutInflater.from(parent.context),
R.layout.chat_room_list_cell, parent, false
)
return ViewHolder(binding)
}
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
(holder as ViewHolder).bind(getItem(position))
}
fun forwardPending(pending: Boolean) {
isForwardPending = pending
notifyItemRangeChanged(0, itemCount)
}
inner class ViewHolder(
private val binding: ChatRoomListCellBinding
) : RecyclerView.ViewHolder(binding.root) {
fun bind(chatRoom: ChatRoom) {
with(binding) {
data = ChatRoomData(chatRoom)
lifecycleOwner = viewLifecycleOwner
// This is for item selection through ListTopBarFragment
selectionListViewModel = selectionViewModel
selectionViewModel.isEditionEnabled.observe(
viewLifecycleOwner
) {
position = bindingAdapterPosition
}
forwardPending = isForwardPending
setClickListener {
if (selectionViewModel.isEditionEnabled.value == true) {
selectionViewModel.onToggleSelect(bindingAdapterPosition)
} else {
selectedChatRoomEvent.value = Event(chatRoom)
}
}
setLongClickListener {
if (selectionViewModel.isEditionEnabled.value == false) {
selectionViewModel.isEditionEnabled.value = true
// Selection will be handled by click listener
true
}
false
}
executePendingBindings()
}
}
}
}
private class ChatRoomDiffCallback : DiffUtil.ItemCallback<ChatRoom>() {
override fun areItemsTheSame(
oldItem: ChatRoom,
newItem: ChatRoom
): Boolean {
return oldItem == newItem
}
override fun areContentsTheSame(
oldItem: ChatRoom,
newItem: ChatRoom
): Boolean {
return false // To force redraw
}
}

View file

@ -1,98 +0,0 @@
/*
* Copyright (c) 2010-2020 Belledonne Communications SARL.
*
* This file is part of linphone-android
* (see https://www.linphone.org).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.linphone.activities.main.chat.adapters
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.databinding.DataBindingUtil
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.MutableLiveData
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.ListAdapter
import androidx.recyclerview.widget.RecyclerView
import org.linphone.R
import org.linphone.activities.main.chat.GroupChatRoomMember
import org.linphone.activities.main.chat.data.GroupInfoParticipantData
import org.linphone.databinding.ChatRoomGroupInfoParticipantCellBinding
import org.linphone.utils.Event
class GroupInfoParticipantsAdapter(
private val viewLifecycleOwner: LifecycleOwner,
private val isEncryptionEnabled: Boolean
) : ListAdapter<GroupInfoParticipantData, RecyclerView.ViewHolder>(ParticipantDiffCallback()) {
private var showAdmin: Boolean = false
val participantRemovedEvent: MutableLiveData<Event<GroupChatRoomMember>> by lazy {
MutableLiveData<Event<GroupChatRoomMember>>()
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val binding: ChatRoomGroupInfoParticipantCellBinding = DataBindingUtil.inflate(
LayoutInflater.from(parent.context),
R.layout.chat_room_group_info_participant_cell, parent, false
)
return ViewHolder(binding)
}
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
(holder as ViewHolder).bind(getItem(position))
}
fun showAdminControls(show: Boolean) {
showAdmin = show
notifyItemRangeChanged(0, itemCount)
}
inner class ViewHolder(
val binding: ChatRoomGroupInfoParticipantCellBinding
) : RecyclerView.ViewHolder(binding.root) {
fun bind(participantViewModel: GroupInfoParticipantData) {
with(binding) {
participantViewModel.showAdminControls.value = showAdmin
data = participantViewModel
lifecycleOwner = viewLifecycleOwner
setRemoveClickListener {
participantRemovedEvent.value = Event(participantViewModel.participant)
}
isEncrypted = isEncryptionEnabled
executePendingBindings()
}
}
}
}
private class ParticipantDiffCallback : DiffUtil.ItemCallback<GroupInfoParticipantData>() {
override fun areItemsTheSame(
oldItem: GroupInfoParticipantData,
newItem: GroupInfoParticipantData
): Boolean {
return oldItem.sipUri == newItem.sipUri
}
override fun areContentsTheSame(
oldItem: GroupInfoParticipantData,
newItem: GroupInfoParticipantData
): Boolean {
return false
}
}

View file

@ -1,124 +0,0 @@
/*
* Copyright (c) 2010-2020 Belledonne Communications SARL.
*
* This file is part of linphone-android
* (see https://www.linphone.org).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.linphone.activities.main.chat.adapters
import android.content.Context
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.databinding.DataBindingUtil
import androidx.lifecycle.LifecycleOwner
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.ListAdapter
import androidx.recyclerview.widget.RecyclerView
import org.linphone.R
import org.linphone.activities.main.chat.data.ImdnParticipantData
import org.linphone.core.ChatMessage
import org.linphone.databinding.ChatRoomImdnParticipantCellBinding
import org.linphone.databinding.ImdnListHeaderBinding
import org.linphone.utils.HeaderAdapter
class ImdnAdapter(
private val viewLifecycleOwner: LifecycleOwner
) : ListAdapter<ImdnParticipantData, RecyclerView.ViewHolder>(ParticipantImdnStateDiffCallback()), HeaderAdapter {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val binding: ChatRoomImdnParticipantCellBinding = DataBindingUtil.inflate(
LayoutInflater.from(parent.context),
R.layout.chat_room_imdn_participant_cell, parent, false
)
return ViewHolder(binding)
}
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
(holder as ViewHolder).bind(getItem(position))
}
inner class ViewHolder(
val binding: ChatRoomImdnParticipantCellBinding
) : RecyclerView.ViewHolder(binding.root) {
fun bind(participantImdnData: ImdnParticipantData) {
with(binding) {
data = participantImdnData
lifecycleOwner = viewLifecycleOwner
executePendingBindings()
}
}
}
override fun displayHeaderForPosition(position: Int): Boolean {
if (position >= itemCount) return false
val participantImdnState = getItem(position)
val previousPosition = position - 1
return if (previousPosition >= 0) {
getItem(previousPosition).imdnState.state != participantImdnState.imdnState.state
} else true
}
override fun getHeaderViewForPosition(context: Context, position: Int): View {
val participantImdnState = getItem(position).imdnState
val binding: ImdnListHeaderBinding = DataBindingUtil.inflate(
LayoutInflater.from(context),
R.layout.imdn_list_header, null, false
)
when (participantImdnState.state) {
ChatMessage.State.Displayed -> {
binding.title = R.string.chat_message_imdn_displayed
binding.textColor = R.color.imdn_read_color
binding.icon = R.drawable.chat_read
}
ChatMessage.State.DeliveredToUser -> {
binding.title = R.string.chat_message_imdn_delivered
binding.textColor = R.color.grey_color
binding.icon = R.drawable.chat_delivered
}
ChatMessage.State.Delivered -> {
binding.title = R.string.chat_message_imdn_sent
binding.textColor = R.color.grey_color
binding.icon = R.drawable.chat_delivered
}
ChatMessage.State.NotDelivered -> {
binding.title = R.string.chat_message_imdn_undelivered
binding.textColor = R.color.red_color
binding.icon = R.drawable.chat_error
}
else -> {}
}
binding.executePendingBindings()
return binding.root
}
}
private class ParticipantImdnStateDiffCallback : DiffUtil.ItemCallback<ImdnParticipantData>() {
override fun areItemsTheSame(
oldItem: ImdnParticipantData,
newItem: ImdnParticipantData
): Boolean {
return oldItem.sipUri == newItem.sipUri
}
override fun areContentsTheSame(
oldItem: ImdnParticipantData,
newItem: ImdnParticipantData
): Boolean {
return false
}
}

View file

@ -1,37 +0,0 @@
/*
* Copyright (c) 2010-2020 Belledonne Communications SARL.
*
* This file is part of linphone-android
* (see https://www.linphone.org).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.linphone.activities.main.chat.data
import org.linphone.utils.FileUtils
class ChatMessageAttachmentData(
val path: String,
private val deleteCallback: (attachment: ChatMessageAttachmentData) -> Unit
) {
val fileName: String = FileUtils.getNameFromFilePath(path)
val isImage: Boolean = FileUtils.isExtensionImage(path)
val isVideo: Boolean = FileUtils.isExtensionVideo(path)
val isAudio: Boolean = FileUtils.isExtensionAudio(path)
val isPdf: Boolean = FileUtils.isExtensionPdf(path)
fun delete() {
deleteCallback(this)
}
}

View file

@ -1,481 +0,0 @@
/*
* Copyright (c) 2010-2020 Belledonne Communications SARL.
*
* This file is part of linphone-android
* (see https://www.linphone.org).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.linphone.activities.main.chat.data
import android.text.Spannable
import android.text.SpannableString
import android.text.Spanned
import android.text.style.UnderlineSpan
import android.widget.Toast
import androidx.lifecycle.MutableLiveData
import androidx.media.AudioFocusRequestCompat
import java.io.BufferedReader
import java.io.FileReader
import java.lang.StringBuilder
import java.text.SimpleDateFormat
import java.util.*
import java.util.concurrent.TimeUnit
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import org.linphone.LinphoneApplication.Companion.coreContext
import org.linphone.R
import org.linphone.core.*
import org.linphone.core.tools.Log
import org.linphone.utils.AppUtils
import org.linphone.utils.FileUtils
import org.linphone.utils.TimestampUtils
class ChatMessageContentData(
private val chatMessage: ChatMessage,
private val contentIndex: Int,
) {
var listener: OnContentClickedListener? = null
val isOutgoing = chatMessage.isOutgoing
val isImage = MutableLiveData<Boolean>()
val isVideo = MutableLiveData<Boolean>()
val isAudio = MutableLiveData<Boolean>()
val isPdf = MutableLiveData<Boolean>()
val isGenericFile = MutableLiveData<Boolean>()
val isVoiceRecording = MutableLiveData<Boolean>()
val isConferenceSchedule = MutableLiveData<Boolean>()
val isConferenceUpdated = MutableLiveData<Boolean>()
val isConferenceCancelled = MutableLiveData<Boolean>()
val fileName = MutableLiveData<String>()
val filePath = MutableLiveData<String>()
val downloadable = MutableLiveData<Boolean>()
val downloadEnabled = MutableLiveData<Boolean>()
val downloadProgressInt = MutableLiveData<Int>()
val downloadProgressString = MutableLiveData<String>()
val downloadLabel = MutableLiveData<Spannable>()
val voiceRecordDuration = MutableLiveData<Int>()
val formattedDuration = MutableLiveData<String>()
val voiceRecordPlayingPosition = MutableLiveData<Int>()
val isVoiceRecordPlaying = MutableLiveData<Boolean>()
val conferenceSubject = MutableLiveData<String>()
val conferenceDescription = MutableLiveData<String>()
val conferenceParticipantCount = MutableLiveData<String>()
val conferenceDate = MutableLiveData<String>()
val conferenceTime = MutableLiveData<String>()
val conferenceDuration = MutableLiveData<String>()
var conferenceAddress = MutableLiveData<String>()
val showDuration = MutableLiveData<Boolean>()
val isAlone: Boolean
get() {
var count = 0
for (content in chatMessage.contents) {
if (content.isFileTransfer || content.isFile) {
count += 1
}
}
return count == 1
}
private var isFileEncrypted: Boolean = false
private var voiceRecordAudioFocusRequest: AudioFocusRequestCompat? = null
private lateinit var voiceRecordingPlayer: Player
private val playerListener = PlayerListener {
Log.i("[Voice Recording] End of file reached")
stopVoiceRecording()
}
private fun getContent(): Content {
return chatMessage.contents[contentIndex]
}
private val chatMessageListener: ChatMessageListenerStub = object : ChatMessageListenerStub() {
override fun onFileTransferProgressIndication(
message: ChatMessage,
c: Content,
offset: Int,
total: Int
) {
if (c.filePath == getContent().filePath) {
val percent = offset * 100 / total
Log.d("[Content] Download progress is: $offset / $total ($percent%)")
downloadProgressInt.value = percent
downloadProgressString.value = "$percent%"
}
}
override fun onMsgStateChanged(message: ChatMessage, state: ChatMessage.State) {
downloadEnabled.value = state != ChatMessage.State.FileTransferInProgress
if (state == ChatMessage.State.FileTransferDone || state == ChatMessage.State.FileTransferError) {
updateContent()
if (state == ChatMessage.State.FileTransferDone) {
Log.i("[Chat Message] File transfer done")
if (!message.isOutgoing && !message.isEphemeral) {
Log.i("[Chat Message] Adding content to media store")
coreContext.addContentToMediaStore(getContent())
}
}
}
}
}
private val scope = CoroutineScope(Dispatchers.IO + SupervisorJob())
init {
isVoiceRecordPlaying.value = false
voiceRecordDuration.value = 0
voiceRecordPlayingPosition.value = 0
updateContent()
chatMessage.addListener(chatMessageListener)
}
fun destroy() {
scope.cancel()
deletePlainFilePath()
chatMessage.removeListener(chatMessageListener)
if (this::voiceRecordingPlayer.isInitialized) {
Log.i("[Voice Recording] Destroying voice record")
stopVoiceRecording()
voiceRecordingPlayer.removeListener(playerListener)
}
}
fun download() {
if (chatMessage.isFileTransferInProgress) {
Log.w("[Content] Another FileTransfer content for this message is currently being downloaded, can't start another one for now")
listener?.onError(R.string.chat_message_download_already_in_progress)
return
}
val content = getContent()
val filePath = content.filePath
if (content.isFileTransfer) {
if (filePath == null || filePath.isEmpty()) {
val contentName = content.name
if (contentName != null) {
val file = FileUtils.getFileStoragePath(contentName)
content.filePath = file.path
Log.i("[Content] Started downloading $contentName into ${content.filePath}")
} else {
Log.e("[Content] Content name is null, can't download it!")
return
}
} else {
Log.w("[Content] File path already set [$filePath] using it (auto download that failed probably)")
}
downloadEnabled.value = false
if (!chatMessage.downloadContent(content)) {
Log.e("[Content] Failed to start content download!")
}
} else {
Log.e("[Content] Content is not a FileTransfer, can't download it!")
}
}
fun openFile() {
listener?.onContentClicked(getContent())
}
private fun deletePlainFilePath() {
val path = filePath.value.orEmpty()
if (path.isNotEmpty() && isFileEncrypted) {
Log.i("[Content] Deleting file used for preview: $path")
FileUtils.deleteFile(path)
filePath.value = ""
}
}
private fun updateContent() {
Log.i("[Content] Updating content")
deletePlainFilePath()
val content = getContent()
isFileEncrypted = content.isFileEncrypted
Log.i("[Content] Is ${if (content.isFile) "file" else "file transfer"} content encrypted ? $isFileEncrypted")
filePath.value = ""
fileName.value = if (content.name.isNullOrEmpty() && !content.filePath.isNullOrEmpty()) {
FileUtils.getNameFromFilePath(content.filePath!!)
} else {
content.name
}
// Display download size and underline text
val fileSize = AppUtils.bytesToDisplayableSize(content.fileSize.toLong())
val spannable = SpannableString("${AppUtils.getString(R.string.chat_message_download_file)} ($fileSize)")
spannable.setSpan(UnderlineSpan(), 0, spannable.length, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
downloadLabel.value = spannable
isImage.value = false
isVideo.value = false
isAudio.value = false
isPdf.value = false
isVoiceRecording.value = false
isConferenceSchedule.value = false
isConferenceUpdated.value = false
isConferenceCancelled.value = false
if (content.isFile || (content.isFileTransfer && chatMessage.isOutgoing)) {
val path = if (isFileEncrypted) {
Log.i("[Content] Content is encrypted, requesting plain file path")
content.exportPlainFile()
} else {
content.filePath ?: ""
}
downloadable.value = content.filePath.orEmpty().isEmpty()
val isVoiceRecord = content.isVoiceRecording
isVoiceRecording.value = isVoiceRecord
val isConferenceIcs = content.isIcalendar
isConferenceSchedule.value = isConferenceIcs
if (path.isNotEmpty()) {
Log.i("[Content] Found displayable content: $path")
filePath.value = path
isImage.value = FileUtils.isExtensionImage(path)
isVideo.value = FileUtils.isExtensionVideo(path) && !isVoiceRecord
isAudio.value = FileUtils.isExtensionAudio(path) && !isVoiceRecord
isPdf.value = FileUtils.isExtensionPdf(path)
if (isVoiceRecord) {
val duration = content.fileDuration // duration is in ms
voiceRecordDuration.value = duration
formattedDuration.value = SimpleDateFormat("mm:ss", Locale.getDefault()).format(duration)
Log.i("[Content] Voice recording duration is ${voiceRecordDuration.value} ($duration)")
} else if (isConferenceIcs) {
parseConferenceInvite(content)
}
} else if (isConferenceIcs) {
Log.i("[Content] Found content with icalendar file")
parseConferenceInvite(content)
} else {
Log.w("[Content] Found ${if (content.isFile) "file" else "file transfer"} content with empty path...")
isImage.value = false
isVideo.value = false
isAudio.value = false
isPdf.value = false
isVoiceRecording.value = false
isConferenceSchedule.value = false
}
} else if (content.isFileTransfer) {
downloadable.value = true
isImage.value = FileUtils.isExtensionImage(fileName.value!!)
isVideo.value = FileUtils.isExtensionVideo(fileName.value!!)
isAudio.value = FileUtils.isExtensionAudio(fileName.value!!)
isPdf.value = FileUtils.isExtensionPdf(fileName.value!!)
isVoiceRecording.value = false
isConferenceSchedule.value = false
} else if (content.isIcalendar) {
Log.i("[Content] Found content with icalendar body")
isConferenceSchedule.value = true
parseConferenceInvite(content)
} else {
Log.w("[Content] Found content that's neither a file or a file transfer")
}
isGenericFile.value = !isPdf.value!! && !isAudio.value!! && !isVideo.value!! && !isImage.value!! && !isVoiceRecording.value!! && !isConferenceSchedule.value!!
downloadEnabled.value = !chatMessage.isFileTransferInProgress
downloadProgressInt.value = 0
downloadProgressString.value = "0%"
}
private fun parseConferenceInvite(content: Content) {
val conferenceInfo = Factory.instance().createConferenceInfoFromIcalendarContent(content)
val conferenceUri = conferenceInfo?.uri?.asStringUriOnly()
if (conferenceInfo != null && conferenceUri != null) {
conferenceAddress.value = conferenceUri!!
Log.i("[Content] Created conference info from ICS with address ${conferenceAddress.value}")
conferenceSubject.value = conferenceInfo.subject
conferenceDescription.value = conferenceInfo.description
val state = conferenceInfo.state
isConferenceUpdated.value = state == ConferenceInfo.State.Updated
isConferenceCancelled.value = state == ConferenceInfo.State.Cancelled
conferenceDate.value = TimestampUtils.dateToString(conferenceInfo.dateTime)
conferenceTime.value = TimestampUtils.timeToString(conferenceInfo.dateTime)
val minutes = conferenceInfo.duration
val hours = TimeUnit.MINUTES.toHours(minutes.toLong())
val remainMinutes = minutes - TimeUnit.HOURS.toMinutes(hours).toInt()
conferenceDuration.value = TimestampUtils.durationToString(hours.toInt(), remainMinutes)
showDuration.value = minutes > 0
conferenceParticipantCount.value = String.format(AppUtils.getString(R.string.conference_invite_participants_count), conferenceInfo.participants.size + 1) // +1 for organizer
} else if (conferenceInfo == null) {
if (content.filePath != null) {
try {
val br = BufferedReader(FileReader(content.filePath))
var line: String?
val textBuilder = StringBuilder()
while (br.readLine().also { line = it } != null) {
textBuilder.append(line)
textBuilder.append('\n')
}
br.close()
Log.e("[Content] Failed to create conference info from ICS file [${content.filePath}]: $textBuilder")
} catch (e: Exception) {
Log.e("[Content] Failed to read content of ICS file [${content.filePath}]: $e")
}
} else {
Log.e("[Content] Failed to create conference info from ICS: ${content.utf8Text}")
}
} else if (conferenceInfo.uri == null) {
Log.e("[Content] Failed to find the conference URI in conference info [$conferenceInfo]")
}
}
fun callConferenceAddress() {
val address = conferenceAddress.value
if (address == null) {
Log.e("[Content] Can't call null conference address!")
return
}
listener?.onCallConference(address, conferenceSubject.value)
}
/** Voice recording specifics */
fun playVoiceRecording() {
Log.i("[Voice Recording] Playing voice record")
if (isPlayerClosed()) {
Log.w("[Voice Recording] Player closed, let's open it first")
initVoiceRecordPlayer()
}
if (AppUtils.isMediaVolumeLow(coreContext.context)) {
Toast.makeText(coreContext.context, R.string.chat_message_voice_recording_playback_low_volume, Toast.LENGTH_LONG).show()
}
if (voiceRecordAudioFocusRequest == null) {
voiceRecordAudioFocusRequest = AppUtils.acquireAudioFocusForVoiceRecordingOrPlayback(
coreContext.context
)
}
voiceRecordingPlayer.start()
isVoiceRecordPlaying.value = true
tickerFlow().onEach {
withContext(Dispatchers.Main) {
voiceRecordPlayingPosition.value = voiceRecordingPlayer.currentPosition
}
}.launchIn(scope)
}
fun pauseVoiceRecording() {
Log.i("[Voice Recording] Pausing voice record")
if (!isPlayerClosed()) {
voiceRecordingPlayer.pause()
}
val request = voiceRecordAudioFocusRequest
if (request != null) {
AppUtils.releaseAudioFocusForVoiceRecordingOrPlayback(coreContext.context, request)
voiceRecordAudioFocusRequest = null
}
isVoiceRecordPlaying.value = false
}
private fun tickerFlow() = flow {
while (isVoiceRecordPlaying.value == true) {
emit(Unit)
delay(100)
}
}
private fun initVoiceRecordPlayer() {
Log.i("[Voice Recording] Creating player for voice record")
// In case no headphones/headset is connected, use speaker sound card to play recordings, otherwise use earpiece
// If none are available, default one will be used
var headphonesCard: String? = null
var speakerCard: String? = null
var earpieceCard: String? = null
for (device in coreContext.core.audioDevices) {
if (device.hasCapability(AudioDevice.Capabilities.CapabilityPlay)) {
when (device.type) {
AudioDevice.Type.Speaker -> {
speakerCard = device.id
}
AudioDevice.Type.Earpiece -> {
earpieceCard = device.id
}
AudioDevice.Type.Headphones, AudioDevice.Type.Headset -> {
headphonesCard = device.id
}
else -> {}
}
}
}
Log.i("[Voice Recording] Found headset/headphones sound card [$headphonesCard], speaker sound card [$speakerCard] and earpiece sound card [$earpieceCard]")
val localPlayer = coreContext.core.createLocalPlayer(headphonesCard ?: speakerCard ?: earpieceCard, null, null)
if (localPlayer != null) {
voiceRecordingPlayer = localPlayer
} else {
Log.e("[Voice Recording] Couldn't create local player!")
return
}
voiceRecordingPlayer.addListener(playerListener)
val path = filePath.value
voiceRecordingPlayer.open(path.orEmpty())
voiceRecordDuration.value = voiceRecordingPlayer.duration
formattedDuration.value = SimpleDateFormat("mm:ss", Locale.getDefault()).format(voiceRecordingPlayer.duration) // is already in milliseconds
Log.i("[Voice Recording] Duration is ${voiceRecordDuration.value} (${voiceRecordingPlayer.duration})")
}
private fun stopVoiceRecording() {
if (!isPlayerClosed()) {
Log.i("[Voice Recording] Stopping voice record")
pauseVoiceRecording()
voiceRecordingPlayer.seek(0)
voiceRecordPlayingPosition.value = 0
voiceRecordingPlayer.close()
}
}
private fun isPlayerClosed(): Boolean {
return !this::voiceRecordingPlayer.isInitialized || voiceRecordingPlayer.state == Player.State.Closed
}
}
interface OnContentClickedListener {
fun onContentClicked(content: Content)
fun onSipAddressClicked(sipUri: String)
fun onWebUrlClicked(url: String)
fun onCallConference(address: String, subject: String?)
fun onError(messageId: Int)
}

View file

@ -1,258 +0,0 @@
/*
* Copyright (c) 2010-2020 Belledonne Communications SARL.
*
* This file is part of linphone-android
* (see https://www.linphone.org).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.linphone.activities.main.chat.data
import android.os.CountDownTimer
import android.text.Spannable
import android.util.Patterns
import androidx.lifecycle.MutableLiveData
import java.util.regex.Pattern
import org.linphone.R
import org.linphone.contact.GenericContactData
import org.linphone.core.ChatMessage
import org.linphone.core.ChatMessageListenerStub
import org.linphone.core.tools.Log
import org.linphone.utils.AppUtils
import org.linphone.utils.PatternClickableSpan
import org.linphone.utils.TimestampUtils
class ChatMessageData(val chatMessage: ChatMessage) : GenericContactData(chatMessage.fromAddress) {
private var contentListener: OnContentClickedListener? = null
val sendInProgress = MutableLiveData<Boolean>()
val transferInProgress = MutableLiveData<Boolean>()
val showImdn = MutableLiveData<Boolean>()
val imdnIcon = MutableLiveData<Int>()
val backgroundRes = MutableLiveData<Int>()
val hideAvatar = MutableLiveData<Boolean>()
val hideTime = MutableLiveData<Boolean>()
val contents = MutableLiveData<ArrayList<ChatMessageContentData>>()
val time = MutableLiveData<String>()
val ephemeralLifetime = MutableLiveData<String>()
val text = MutableLiveData<Spannable>()
val replyData = MutableLiveData<ChatMessageData>()
val isDisplayed = MutableLiveData<Boolean>()
val isOutgoing = chatMessage.isOutgoing
var hasPreviousMessage = false
var hasNextMessage = false
private var countDownTimer: CountDownTimer? = null
private val listener = object : ChatMessageListenerStub() {
override fun onMsgStateChanged(message: ChatMessage, state: ChatMessage.State) {
time.value = TimestampUtils.toString(chatMessage.time)
updateChatMessageState(state)
}
override fun onEphemeralMessageTimerStarted(message: ChatMessage) {
updateEphemeralTimer()
}
}
init {
chatMessage.addListener(listener)
backgroundRes.value = if (chatMessage.isOutgoing) R.drawable.chat_bubble_outgoing_full else R.drawable.chat_bubble_incoming_full
hideAvatar.value = false
if (chatMessage.isReply) {
val reply = chatMessage.replyMessage
if (reply != null) {
Log.i("[Chat Message Data] Message is a reply of message id [${chatMessage.replyMessageId}] sent by [${chatMessage.replyMessageSenderAddress?.asStringUriOnly()}]")
replyData.value = ChatMessageData(reply)
}
}
time.value = TimestampUtils.toString(chatMessage.time)
updateEphemeralTimer()
updateChatMessageState(chatMessage.state)
updateContentsList()
}
override fun destroy() {
super.destroy()
if (chatMessage.isReply) {
replyData.value?.destroy()
}
contents.value.orEmpty().forEach(ChatMessageContentData::destroy)
chatMessage.removeListener(listener)
contentListener = null
}
fun updateBubbleBackground(hasPrevious: Boolean, hasNext: Boolean) {
hasPreviousMessage = hasPrevious
hasNextMessage = hasNext
hideTime.value = false
hideAvatar.value = false
if (hasPrevious) {
hideTime.value = true
}
if (chatMessage.isOutgoing) {
if (hasNext && hasPrevious) {
backgroundRes.value = R.drawable.chat_bubble_outgoing_split_2
} else if (hasNext) {
backgroundRes.value = R.drawable.chat_bubble_outgoing_split_1
} else if (hasPrevious) {
backgroundRes.value = R.drawable.chat_bubble_outgoing_split_3
} else {
backgroundRes.value = R.drawable.chat_bubble_outgoing_full
}
} else {
if (hasNext && hasPrevious) {
hideAvatar.value = true
backgroundRes.value = R.drawable.chat_bubble_incoming_split_2
} else if (hasNext) {
backgroundRes.value = R.drawable.chat_bubble_incoming_split_1
} else if (hasPrevious) {
hideAvatar.value = true
backgroundRes.value = R.drawable.chat_bubble_incoming_split_3
} else {
backgroundRes.value = R.drawable.chat_bubble_incoming_full
}
}
}
fun setContentClickListener(listener: OnContentClickedListener) {
contentListener = listener
for (data in contents.value.orEmpty()) {
data.listener = listener
}
}
private fun updateChatMessageState(state: ChatMessage.State) {
transferInProgress.value = state == ChatMessage.State.FileTransferInProgress
sendInProgress.value = state == ChatMessage.State.InProgress || state == ChatMessage.State.FileTransferInProgress
showImdn.value = when (state) {
ChatMessage.State.DeliveredToUser, ChatMessage.State.Displayed, ChatMessage.State.NotDelivered -> true
else -> false
}
imdnIcon.value = when (state) {
ChatMessage.State.DeliveredToUser -> R.drawable.chat_delivered
ChatMessage.State.Displayed -> R.drawable.chat_read
else -> R.drawable.chat_error
}
isDisplayed.value = state == ChatMessage.State.Displayed
}
private fun updateContentsList() {
contents.value.orEmpty().forEach(ChatMessageContentData::destroy)
val list = arrayListOf<ChatMessageContentData>()
val contentsList = chatMessage.contents
for (index in contentsList.indices) {
val content = contentsList[index]
if (content.isFileTransfer || content.isFile || content.isIcalendar) {
val data = ChatMessageContentData(chatMessage, index)
data.listener = contentListener
list.add(data)
} else if (content.isText) {
val spannable = Spannable.Factory.getInstance().newSpannable(content.utf8Text?.trim())
text.value = PatternClickableSpan()
.add(
Pattern.compile("(?:<?sips?:)?[^@\\s]+(?:@([^\\s]+))+"),
object : PatternClickableSpan.SpannableClickedListener {
override fun onSpanClicked(text: String) {
Log.i("[Chat Message Data] Clicked on SIP URI: $text")
contentListener?.onSipAddressClicked(text)
}
}
)
.add(
Patterns.WEB_URL,
object : PatternClickableSpan.SpannableClickedListener {
override fun onSpanClicked(text: String) {
Log.i("[Chat Message Data] Clicked on web URL: $text")
contentListener?.onWebUrlClicked(text)
}
}
)
.add(
Patterns.PHONE,
object : PatternClickableSpan.SpannableClickedListener {
override fun onSpanClicked(text: String) {
Log.i("[Chat Message Data] Clicked on phone number: $text")
contentListener?.onSipAddressClicked(text)
}
}
).build(spannable)
} else {
Log.e("[Chat Message Data] Unexpected content with type: ${content.type}/${content.subtype}")
}
}
contents.value = list
}
private fun updateEphemeralTimer() {
if (chatMessage.isEphemeral) {
if (chatMessage.ephemeralExpireTime == 0L) {
// This means the message hasn't been read by all participants yet, so the countdown hasn't started
// In this case we simply display the configured value for lifetime
ephemeralLifetime.value = formatLifetime(chatMessage.ephemeralLifetime)
} else {
// Countdown has started, display remaining time
val remaining = chatMessage.ephemeralExpireTime - (System.currentTimeMillis() / 1000)
ephemeralLifetime.value = formatLifetime(remaining)
if (countDownTimer == null) {
countDownTimer = object : CountDownTimer(remaining * 1000, 1000) {
override fun onFinish() {}
override fun onTick(millisUntilFinished: Long) {
ephemeralLifetime.postValue(formatLifetime(millisUntilFinished / 1000))
}
}
countDownTimer?.start()
}
}
}
}
private fun formatLifetime(seconds: Long): String {
val days = seconds / 86400
return when {
days >= 1L -> AppUtils.getStringWithPlural(R.plurals.days, days.toInt())
else -> String.format("%02d:%02d:%02d", seconds / 3600, (seconds % 3600) / 60, (seconds % 60))
}
}
}

View file

@ -1,183 +0,0 @@
/*
* Copyright (c) 2010-2022 Belledonne Communications SARL.
*
* This file is part of linphone-android
* (see https://www.linphone.org).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.linphone.activities.main.chat.data
import android.graphics.Typeface
import android.text.SpannableStringBuilder
import android.text.style.StyleSpan
import androidx.lifecycle.MutableLiveData
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import org.linphone.LinphoneApplication.Companion.coreContext
import org.linphone.LinphoneApplication.Companion.corePreferences
import org.linphone.R
import org.linphone.contact.ContactDataInterface
import org.linphone.core.*
import org.linphone.core.tools.Log
import org.linphone.utils.AppUtils
import org.linphone.utils.LinphoneUtils
import org.linphone.utils.TimestampUtils
class ChatRoomData(private val chatRoom: ChatRoom) : ContactDataInterface {
override val contact: MutableLiveData<Friend> = MutableLiveData<Friend>()
override val displayName: MutableLiveData<String> = MutableLiveData<String>()
override val securityLevel: MutableLiveData<ChatRoomSecurityLevel> = MutableLiveData<ChatRoomSecurityLevel>()
override val showGroupChatAvatar: Boolean
get() = conferenceChatRoom && !oneToOneChatRoom
override val coroutineScope: CoroutineScope = coreContext.coroutineScope
val unreadMessagesCount = MutableLiveData<Int>()
val subject = MutableLiveData<String>()
val securityLevelIcon = MutableLiveData<Int>()
val securityLevelContentDescription = MutableLiveData<Int>()
val ephemeralEnabled = MutableLiveData<Boolean>()
val lastUpdate = MutableLiveData<String>()
val lastMessageText = MutableLiveData<SpannableStringBuilder>()
val notificationsMuted = MutableLiveData<Boolean>()
private val basicChatRoom: Boolean by lazy {
chatRoom.hasCapability(ChatRoomCapabilities.Basic.toInt())
}
val oneToOneChatRoom: Boolean by lazy {
chatRoom.hasCapability(ChatRoomCapabilities.OneToOne.toInt())
}
private val conferenceChatRoom: Boolean by lazy {
chatRoom.hasCapability(ChatRoomCapabilities.Conference.toInt())
}
val encryptedChatRoom: Boolean by lazy {
chatRoom.hasCapability(ChatRoomCapabilities.Encrypted.toInt())
}
init {
unreadMessagesCount.value = chatRoom.unreadMessagesCount
subject.value = chatRoom.subject
updateSecurityIcon()
ephemeralEnabled.value = chatRoom.isEphemeralEnabled
contactLookup()
formatLastMessage(chatRoom.lastMessageInHistory)
notificationsMuted.value = areNotificationsMuted()
}
private fun updateSecurityIcon() {
val level = chatRoom.securityLevel
securityLevel.value = level
securityLevelIcon.value = when (level) {
ChatRoomSecurityLevel.Safe -> R.drawable.security_2_indicator
ChatRoomSecurityLevel.Encrypted -> R.drawable.security_1_indicator
else -> R.drawable.security_alert_indicator
}
securityLevelContentDescription.value = when (level) {
ChatRoomSecurityLevel.Safe -> R.string.content_description_security_level_safe
ChatRoomSecurityLevel.Encrypted -> R.string.content_description_security_level_encrypted
else -> R.string.content_description_security_level_unsafe
}
}
private fun contactLookup() {
displayName.value = when {
basicChatRoom -> LinphoneUtils.getDisplayName(
chatRoom.peerAddress
)
oneToOneChatRoom -> LinphoneUtils.getDisplayName(
chatRoom.participants.firstOrNull()?.address ?: chatRoom.peerAddress
)
conferenceChatRoom -> chatRoom.subject.orEmpty()
else -> chatRoom.peerAddress.asStringUriOnly()
}
if (oneToOneChatRoom) {
searchMatchingContact()
}
}
private fun searchMatchingContact() {
val remoteAddress = if (basicChatRoom) {
chatRoom.peerAddress
} else {
if (chatRoom.participants.isNotEmpty()) {
chatRoom.participants[0].address
} else {
Log.e("[Chat Room] ${chatRoom.peerAddress} doesn't have any participant (state ${chatRoom.state})!")
null
}
}
if (remoteAddress != null) {
contact.value = coreContext.contactsManager.findContactByAddress(remoteAddress)
}
}
private fun formatLastMessage(msg: ChatMessage?) {
val lastUpdateTime = chatRoom.lastUpdateTime
lastUpdate.value = "00:00"
coroutineScope.launch {
withContext(Dispatchers.IO) {
lastUpdate.postValue(TimestampUtils.toString(lastUpdateTime, true))
}
}
val builder = SpannableStringBuilder()
if (msg == null) {
lastMessageText.value = builder
return
}
val sender: String =
coreContext.contactsManager.findContactByAddress(msg.fromAddress)?.name
?: LinphoneUtils.getDisplayName(msg.fromAddress)
builder.append(sender)
builder.append(": ")
for (content in msg.contents) {
if (content.isIcalendar) {
val body = AppUtils.getString(R.string.conference_invitation)
builder.append(body)
builder.setSpan(StyleSpan(Typeface.ITALIC), builder.length - body.length, builder.length, 0)
} else if (content.isFile || content.isFileTransfer) {
builder.append(content.name + " ")
} else if (content.isText) {
builder.append(content.utf8Text + " ")
}
}
builder.trim()
lastMessageText.value = builder
}
private fun areNotificationsMuted(): Boolean {
val id = LinphoneUtils.getChatRoomId(chatRoom.localAddress, chatRoom.peerAddress)
return corePreferences.chatRoomMuted(id)
}
}

View file

@ -1,49 +0,0 @@
/*
* Copyright (c) 2010-2020 Belledonne Communications SARL.
*
* This file is part of linphone-android
* (see https://www.linphone.org).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.linphone.activities.main.chat.data
import org.linphone.LinphoneApplication.Companion.coreContext
import org.linphone.R
import org.linphone.core.ChatRoomSecurityLevel
import org.linphone.core.ParticipantDevice
class DevicesListChildData(private val device: ParticipantDevice) {
val deviceName: String = device.name.orEmpty()
val securityLevelIcon: Int by lazy {
when (device.securityLevel) {
ChatRoomSecurityLevel.Safe -> R.drawable.security_2_indicator
ChatRoomSecurityLevel.Encrypted -> R.drawable.security_1_indicator
else -> R.drawable.security_alert_indicator
}
}
val securityContentDescription: Int by lazy {
when (device.securityLevel) {
ChatRoomSecurityLevel.Safe -> R.string.content_description_security_level_safe
ChatRoomSecurityLevel.Encrypted -> R.string.content_description_security_level_encrypted
else -> R.string.content_description_security_level_unsafe
}
}
fun onClick() {
coreContext.startCall(device.address, forceZRTP = true)
}
}

View file

@ -1,73 +0,0 @@
/*
* Copyright (c) 2010-2020 Belledonne Communications SARL.
*
* This file is part of linphone-android
* (see https://www.linphone.org).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.linphone.activities.main.chat.data
import androidx.lifecycle.MutableLiveData
import org.linphone.LinphoneApplication.Companion.coreContext
import org.linphone.R
import org.linphone.contact.GenericContactData
import org.linphone.core.ChatRoomSecurityLevel
import org.linphone.core.Participant
import org.linphone.utils.LinphoneUtils
class DevicesListGroupData(private val participant: Participant) : GenericContactData(participant.address) {
private val device = if (participant.devices.isEmpty()) null else participant.devices.first()
val securityLevelIcon: Int by lazy {
when (device?.securityLevel) {
ChatRoomSecurityLevel.Safe -> R.drawable.security_2_indicator
ChatRoomSecurityLevel.Encrypted -> R.drawable.security_1_indicator
else -> R.drawable.security_alert_indicator
}
}
val securityLevelContentDescription: Int by lazy {
when (device?.securityLevel) {
ChatRoomSecurityLevel.Safe -> R.string.content_description_security_level_safe
ChatRoomSecurityLevel.Encrypted -> R.string.content_description_security_level_encrypted
else -> R.string.content_description_security_level_unsafe
}
}
val sipUri: String get() = LinphoneUtils.getDisplayableAddress(participant.address)
val isExpanded = MutableLiveData<Boolean>()
val devices = MutableLiveData<ArrayList<DevicesListChildData>>()
init {
securityLevel.value = participant.securityLevel
isExpanded.value = false
val list = arrayListOf<DevicesListChildData>()
for (device in participant.devices) {
list.add(DevicesListChildData((device)))
}
devices.value = list
}
fun toggleExpanded() {
isExpanded.value = isExpanded.value != true
}
fun onClick() {
if (device?.address != null) coreContext.startCall(device.address, forceZRTP = true)
}
}

View file

@ -1,105 +0,0 @@
/*
* Copyright (c) 2010-2020 Belledonne Communications SARL.
*
* This file is part of linphone-android
* (see https://www.linphone.org).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.linphone.activities.main.chat.data
import android.content.Context
import androidx.lifecycle.MutableLiveData
import org.linphone.LinphoneApplication.Companion.coreContext
import org.linphone.R
import org.linphone.contact.GenericContactData
import org.linphone.core.EventLog
class EventData(private val eventLog: EventLog) : GenericContactData(
if (eventLog.type == EventLog.Type.ConferenceSecurityEvent) {
eventLog.securityEventFaultyDeviceAddress!!
} else {
if (eventLog.participantAddress == null) {
eventLog.peerAddress!!
} else {
eventLog.participantAddress!!
}
}
) {
val text = MutableLiveData<String>()
val isSecurity: Boolean by lazy {
when (eventLog.type) {
EventLog.Type.ConferenceSecurityEvent -> true
else -> false
}
}
val isGroupLeft: Boolean by lazy {
when (eventLog.type) {
EventLog.Type.ConferenceTerminated -> true
else -> false
}
}
init {
updateEventText()
}
private fun getName(): String {
return contact.value?.name ?: displayName.value ?: ""
}
private fun updateEventText() {
val context: Context = coreContext.context
text.value = when (eventLog.type) {
EventLog.Type.ConferenceCreated -> context.getString(R.string.chat_event_conference_created)
EventLog.Type.ConferenceTerminated -> context.getString(R.string.chat_event_conference_destroyed)
EventLog.Type.ConferenceParticipantAdded -> context.getString(R.string.chat_event_participant_added).format(getName())
EventLog.Type.ConferenceParticipantRemoved -> context.getString(R.string.chat_event_participant_removed).format(getName())
EventLog.Type.ConferenceSubjectChanged -> context.getString(R.string.chat_event_subject_changed).format(eventLog.subject)
EventLog.Type.ConferenceParticipantSetAdmin -> context.getString(R.string.chat_event_admin_set).format(getName())
EventLog.Type.ConferenceParticipantUnsetAdmin -> context.getString(R.string.chat_event_admin_unset).format(getName())
EventLog.Type.ConferenceParticipantDeviceAdded -> context.getString(R.string.chat_event_device_added).format(getName())
EventLog.Type.ConferenceParticipantDeviceRemoved -> context.getString(R.string.chat_event_device_removed).format(getName())
EventLog.Type.ConferenceSecurityEvent -> {
val name = getName()
when (eventLog.securityEventType) {
EventLog.SecurityEventType.EncryptionIdentityKeyChanged -> context.getString(R.string.chat_security_event_lime_identity_key_changed).format(name)
EventLog.SecurityEventType.ManInTheMiddleDetected -> context.getString(R.string.chat_security_event_man_in_the_middle_detected).format(name)
EventLog.SecurityEventType.SecurityLevelDowngraded -> context.getString(R.string.chat_security_event_security_level_downgraded).format(name)
EventLog.SecurityEventType.ParticipantMaxDeviceCountExceeded -> context.getString(R.string.chat_security_event_participant_max_count_exceeded).format(name)
else -> "Unexpected security event for $name: ${eventLog.securityEventType}"
}
}
EventLog.Type.ConferenceEphemeralMessageDisabled -> context.getString(R.string.chat_event_ephemeral_disabled)
EventLog.Type.ConferenceEphemeralMessageEnabled -> context.getString(R.string.chat_event_ephemeral_enabled).format(formatEphemeralExpiration(context, eventLog.ephemeralMessageLifetime))
EventLog.Type.ConferenceEphemeralMessageLifetimeChanged -> context.getString(R.string.chat_event_ephemeral_lifetime_changed).format(formatEphemeralExpiration(context, eventLog.ephemeralMessageLifetime))
else -> "Unexpected event: ${eventLog.type}"
}
}
private fun formatEphemeralExpiration(context: Context, duration: Long): String {
return when (duration) {
0L -> context.getString(R.string.chat_room_ephemeral_message_disabled)
60L -> context.getString(R.string.chat_room_ephemeral_message_one_minute)
3600L -> context.getString(R.string.chat_room_ephemeral_message_one_hour)
86400L -> context.getString(R.string.chat_room_ephemeral_message_one_day)
259200L -> context.getString(R.string.chat_room_ephemeral_message_three_days)
604800L -> context.getString(R.string.chat_room_ephemeral_message_one_week)
else -> "Unexpected duration"
}
}
}

View file

@ -1,71 +0,0 @@
/*
* Copyright (c) 2010-2020 Belledonne Communications SARL.
*
* This file is part of linphone-android
* (see https://www.linphone.org).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.linphone.activities.main.chat.data
import androidx.lifecycle.MutableLiveData
import org.linphone.R
import org.linphone.activities.main.chat.GroupChatRoomMember
import org.linphone.contact.GenericContactData
import org.linphone.core.ChatRoomSecurityLevel
import org.linphone.utils.LinphoneUtils
class GroupInfoParticipantData(val participant: GroupChatRoomMember) : GenericContactData(participant.address) {
val sipUri: String get() = LinphoneUtils.getDisplayableAddress(participant.address)
val isAdmin = MutableLiveData<Boolean>()
val showAdminControls = MutableLiveData<Boolean>()
// A participant not yet added to a group can't be set admin at the same time it's added
val canBeSetAdmin = MutableLiveData<Boolean>()
val securityLevelIcon: Int by lazy {
when (participant.securityLevel) {
ChatRoomSecurityLevel.Safe -> R.drawable.security_2_indicator
ChatRoomSecurityLevel.Encrypted -> R.drawable.security_1_indicator
else -> R.drawable.security_alert_indicator
}
}
val securityLevelContentDescription: Int by lazy {
when (participant.securityLevel) {
ChatRoomSecurityLevel.Safe -> R.string.content_description_security_level_safe
ChatRoomSecurityLevel.Encrypted -> R.string.content_description_security_level_encrypted
else -> R.string.content_description_security_level_unsafe
}
}
init {
securityLevel.value = participant.securityLevel
isAdmin.value = participant.isAdmin
showAdminControls.value = false
canBeSetAdmin.value = participant.canBeSetAdmin
}
fun setAdmin() {
isAdmin.value = true
participant.isAdmin = true
}
fun unSetAdmin() {
isAdmin.value = false
participant.isAdmin = false
}
}

View file

@ -1,30 +0,0 @@
/*
* Copyright (c) 2010-2020 Belledonne Communications SARL.
*
* This file is part of linphone-android
* (see https://www.linphone.org).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.linphone.activities.main.chat.data
import org.linphone.contact.GenericContactData
import org.linphone.core.ParticipantImdnState
import org.linphone.utils.TimestampUtils
class ImdnParticipantData(val imdnState: ParticipantImdnState) : GenericContactData(imdnState.participant.address) {
val sipUri: String = imdnState.participant.address.asStringUriOnly()
val time: String = TimestampUtils.toString(imdnState.stateChangeTime)
}

View file

@ -1,190 +0,0 @@
/*
* Copyright (c) 2010-2020 Belledonne Communications SARL.
*
* This file is part of linphone-android
* (see https://www.linphone.org).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.linphone.activities.main.chat.fragments
import android.content.pm.PackageManager
import android.os.Bundle
import android.view.View
import androidx.lifecycle.ViewModelProvider
import androidx.recyclerview.widget.LinearLayoutManager
import org.linphone.LinphoneApplication.Companion.coreContext
import org.linphone.LinphoneApplication.Companion.corePreferences
import org.linphone.R
import org.linphone.activities.main.MainActivity
import org.linphone.activities.main.chat.viewmodels.ChatRoomCreationViewModel
import org.linphone.activities.main.fragments.SecureFragment
import org.linphone.activities.navigateToChatRoom
import org.linphone.activities.navigateToGroupInfo
import org.linphone.contact.ContactsSelectionAdapter
import org.linphone.core.tools.Log
import org.linphone.databinding.ChatRoomCreationFragmentBinding
import org.linphone.utils.AppUtils
import org.linphone.utils.LinphoneUtils
import org.linphone.utils.PermissionHelper
class ChatRoomCreationFragment : SecureFragment<ChatRoomCreationFragmentBinding>() {
private lateinit var viewModel: ChatRoomCreationViewModel
private lateinit var adapter: ContactsSelectionAdapter
override fun getLayoutId(): Int = R.layout.chat_room_creation_fragment
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding.lifecycleOwner = viewLifecycleOwner
useMaterialSharedAxisXForwardAnimation = sharedViewModel.isSlidingPaneSlideable.value == false
val createGroup = arguments?.getBoolean("createGroup") ?: false
viewModel = ViewModelProvider(this)[ChatRoomCreationViewModel::class.java]
viewModel.createGroupChat.value = createGroup
viewModel.isEncrypted.value = sharedViewModel.createEncryptedChatRoom
binding.viewModel = viewModel
adapter = ContactsSelectionAdapter(viewLifecycleOwner)
adapter.setGroupChatCapabilityRequired(viewModel.createGroupChat.value == true)
adapter.setLimeCapabilityRequired(viewModel.isEncrypted.value == true)
binding.contactsList.adapter = adapter
val layoutManager = LinearLayoutManager(requireContext())
binding.contactsList.layoutManager = layoutManager
// Divider between items
binding.contactsList.addItemDecoration(AppUtils.getDividerDecoration(requireContext(), layoutManager))
binding.back.visibility = if (resources.getBoolean(R.bool.isTablet)) View.INVISIBLE else View.VISIBLE
binding.setAllContactsToggleClickListener {
viewModel.sipContactsSelected.value = false
}
binding.setSipContactsToggleClickListener {
viewModel.sipContactsSelected.value = true
}
viewModel.contactsList.observe(
viewLifecycleOwner
) {
adapter.submitList(it)
}
viewModel.isEncrypted.observe(
viewLifecycleOwner
) {
adapter.setLimeCapabilityRequired(it)
}
viewModel.sipContactsSelected.observe(
viewLifecycleOwner
) {
viewModel.applyFilter()
}
viewModel.selectedAddresses.observe(
viewLifecycleOwner
) {
adapter.updateSelectedAddresses(it)
}
viewModel.chatRoomCreatedEvent.observe(
viewLifecycleOwner
) {
it.consume { chatRoom ->
sharedViewModel.selectedChatRoom.value = chatRoom
navigateToChatRoom(AppUtils.createBundleWithSharedTextAndFiles(sharedViewModel))
}
}
viewModel.filter.observe(
viewLifecycleOwner
) {
viewModel.applyFilter()
}
adapter.selectedContact.observe(
viewLifecycleOwner
) {
it.consume { searchResult ->
if (createGroup) {
viewModel.toggleSelectionForSearchResult(searchResult)
} else {
viewModel.createOneToOneChat(searchResult)
}
}
}
addParticipantsFromSharedViewModel()
// Next button is only used to go to group chat info fragment
binding.setNextClickListener {
sharedViewModel.createEncryptedChatRoom = viewModel.isEncrypted.value == true
sharedViewModel.chatRoomParticipants.value = viewModel.selectedAddresses.value
navigateToGroupInfo()
}
viewModel.onMessageToNotifyEvent.observe(
viewLifecycleOwner
) {
it.consume { messageResourceId ->
(activity as MainActivity).showSnackBar(messageResourceId)
}
}
if (corePreferences.enableNativeAddressBookIntegration) {
if (!PermissionHelper.get().hasReadContactsPermission()) {
Log.i("[Chat Room Creation] Asking for READ_CONTACTS permission")
requestPermissions(arrayOf(android.Manifest.permission.READ_CONTACTS), 0)
}
}
}
@Deprecated("Deprecated in Java")
override fun onRequestPermissionsResult(
requestCode: Int,
permissions: Array<out String>,
grantResults: IntArray
) {
if (requestCode == 0) {
val granted = grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED
if (granted) {
Log.i("[Chat Room Creation] READ_CONTACTS permission granted")
coreContext.fetchContacts()
} else {
Log.w("[Chat Room Creation] READ_CONTACTS permission denied")
}
}
}
override fun onResume() {
super.onResume()
viewModel.secureChatAvailable.value = LinphoneUtils.isEndToEndEncryptedChatAvailable()
}
private fun addParticipantsFromSharedViewModel() {
val participants = sharedViewModel.chatRoomParticipants.value
if (participants != null && participants.size > 0) {
viewModel.selectedAddresses.value = participants
}
}
}

View file

@ -1,64 +0,0 @@
/*
* Copyright (c) 2010-2020 Belledonne Communications SARL.
*
* This file is part of linphone-android
* (see https://www.linphone.org).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.linphone.activities.main.chat.fragments
import android.os.Bundle
import android.view.View
import androidx.lifecycle.ViewModelProvider
import androidx.navigation.fragment.findNavController
import org.linphone.R
import org.linphone.activities.main.chat.viewmodels.DevicesListViewModel
import org.linphone.activities.main.chat.viewmodels.DevicesListViewModelFactory
import org.linphone.activities.main.fragments.SecureFragment
import org.linphone.core.tools.Log
import org.linphone.databinding.ChatRoomDevicesFragmentBinding
class DevicesFragment : SecureFragment<ChatRoomDevicesFragmentBinding>() {
private lateinit var listViewModel: DevicesListViewModel
override fun getLayoutId(): Int = R.layout.chat_room_devices_fragment
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding.lifecycleOwner = viewLifecycleOwner
val chatRoom = sharedViewModel.selectedChatRoom.value
if (chatRoom == null) {
Log.e("[Devices] Chat room is null, aborting!")
findNavController().navigateUp()
return
}
isSecure = chatRoom.currentParams.isEncryptionEnabled
listViewModel = ViewModelProvider(
this,
DevicesListViewModelFactory(chatRoom)
)[DevicesListViewModel::class.java]
binding.viewModel = listViewModel
}
override fun onResume() {
super.onResume()
listViewModel.updateParticipants()
}
}

View file

@ -1,66 +0,0 @@
/*
* Copyright (c) 2010-2020 Belledonne Communications SARL.
*
* This file is part of linphone-android
* (see https://www.linphone.org).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.linphone.activities.main.chat.fragments
import android.os.Bundle
import android.view.View
import androidx.lifecycle.ViewModelProvider
import androidx.navigation.fragment.findNavController
import org.linphone.R
import org.linphone.activities.main.chat.viewmodels.EphemeralViewModel
import org.linphone.activities.main.chat.viewmodels.EphemeralViewModelFactory
import org.linphone.activities.main.fragments.SecureFragment
import org.linphone.core.tools.Log
import org.linphone.databinding.ChatRoomEphemeralFragmentBinding
import org.linphone.utils.Event
class EphemeralFragment : SecureFragment<ChatRoomEphemeralFragmentBinding>() {
private lateinit var viewModel: EphemeralViewModel
override fun getLayoutId(): Int {
return R.layout.chat_room_ephemeral_fragment
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
isSecure = true
binding.lifecycleOwner = viewLifecycleOwner
val chatRoom = sharedViewModel.selectedChatRoom.value
if (chatRoom == null) {
Log.e("[Ephemeral] Chat room is null, aborting!")
findNavController().navigateUp()
return
}
viewModel = ViewModelProvider(
this,
EphemeralViewModelFactory(chatRoom)
)[EphemeralViewModel::class.java]
binding.viewModel = viewModel
binding.setValidClickListener {
viewModel.updateChatRoomEphemeralDuration()
sharedViewModel.refreshChatRoomInListEvent.value = Event(true)
goBack()
}
}
}

View file

@ -1,231 +0,0 @@
/*
* Copyright (c) 2010-2020 Belledonne Communications SARL.
*
* This file is part of linphone-android
* (see https://www.linphone.org).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.linphone.activities.main.chat.fragments
import android.app.Dialog
import android.os.Bundle
import android.view.View
import androidx.lifecycle.ViewModelProvider
import androidx.recyclerview.widget.LinearLayoutManager
import org.linphone.LinphoneApplication.Companion.corePreferences
import org.linphone.R
import org.linphone.activities.main.MainActivity
import org.linphone.activities.main.chat.GroupChatRoomMember
import org.linphone.activities.main.chat.adapters.GroupInfoParticipantsAdapter
import org.linphone.activities.main.chat.data.GroupInfoParticipantData
import org.linphone.activities.main.chat.viewmodels.GroupInfoViewModel
import org.linphone.activities.main.chat.viewmodels.GroupInfoViewModelFactory
import org.linphone.activities.main.fragments.SecureFragment
import org.linphone.activities.main.viewmodels.DialogViewModel
import org.linphone.activities.navigateToChatRoom
import org.linphone.activities.navigateToChatRoomCreation
import org.linphone.core.Address
import org.linphone.core.ChatRoom
import org.linphone.core.ChatRoomCapabilities
import org.linphone.databinding.ChatRoomGroupInfoFragmentBinding
import org.linphone.utils.AppUtils
import org.linphone.utils.DialogUtils
class GroupInfoFragment : SecureFragment<ChatRoomGroupInfoFragmentBinding>() {
private lateinit var viewModel: GroupInfoViewModel
private lateinit var adapter: GroupInfoParticipantsAdapter
private var meAdminStatusChangedDialog: Dialog? = null
override fun getLayoutId(): Int = R.layout.chat_room_group_info_fragment
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding.lifecycleOwner = viewLifecycleOwner
val chatRoom: ChatRoom? = sharedViewModel.selectedGroupChatRoom.value
isSecure = chatRoom?.currentParams?.isEncryptionEnabled ?: false
viewModel = ViewModelProvider(
this,
GroupInfoViewModelFactory(chatRoom)
)[GroupInfoViewModel::class.java]
binding.viewModel = viewModel
viewModel.isEncrypted.value = sharedViewModel.createEncryptedChatRoom
adapter = GroupInfoParticipantsAdapter(
viewLifecycleOwner,
chatRoom?.hasCapability(ChatRoomCapabilities.Encrypted.toInt()) ?: (viewModel.isEncrypted.value == true)
)
binding.participants.adapter = adapter
val layoutManager = LinearLayoutManager(requireContext())
binding.participants.layoutManager = layoutManager
// Divider between items
binding.participants.addItemDecoration(AppUtils.getDividerDecoration(requireContext(), layoutManager))
viewModel.participants.observe(
viewLifecycleOwner
) {
adapter.submitList(it)
}
viewModel.isMeAdmin.observe(
viewLifecycleOwner
) { isMeAdmin ->
adapter.showAdminControls(isMeAdmin && chatRoom != null)
}
viewModel.meAdminChangedEvent.observe(
viewLifecycleOwner
) {
it.consume { isMeAdmin ->
showMeAdminStateChanged(isMeAdmin)
}
}
adapter.participantRemovedEvent.observe(
viewLifecycleOwner
) {
it.consume { participant ->
viewModel.removeParticipant(participant)
}
}
addParticipantsFromSharedViewModel()
viewModel.createdChatRoomEvent.observe(
viewLifecycleOwner
) {
it.consume { chatRoom ->
goToChatRoom(chatRoom, true)
}
}
viewModel.updatedChatRoomEvent.observe(
viewLifecycleOwner
) {
it.consume { chatRoom ->
goToChatRoom(chatRoom, false)
}
}
binding.setNextClickListener {
if (viewModel.chatRoom != null) {
viewModel.updateRoom()
} else {
viewModel.createChatRoom()
}
}
binding.setParticipantsClickListener {
sharedViewModel.createEncryptedChatRoom = corePreferences.forceEndToEndEncryptedChat || viewModel.isEncrypted.value == true
val list = arrayListOf<Address>()
for (participant in viewModel.participants.value.orEmpty()) {
list.add(participant.participant.address)
}
sharedViewModel.chatRoomParticipants.value = list
sharedViewModel.chatRoomSubject = viewModel.subject.value.orEmpty()
val args = Bundle()
args.putBoolean("createGroup", true)
navigateToChatRoomCreation(args)
}
binding.setLeaveClickListener {
val dialogViewModel = DialogViewModel(getString(R.string.chat_room_group_info_leave_dialog_message))
val dialog: Dialog = DialogUtils.getDialog(requireContext(), dialogViewModel)
dialogViewModel.showDeleteButton(
{
viewModel.leaveGroup()
dialog.dismiss()
},
getString(R.string.chat_room_group_info_leave_dialog_button)
)
dialogViewModel.showCancelButton {
dialog.dismiss()
}
dialog.show()
}
viewModel.onMessageToNotifyEvent.observe(
viewLifecycleOwner
) {
it.consume { messageResourceId ->
(activity as MainActivity).showSnackBar(messageResourceId)
}
}
}
private fun addParticipantsFromSharedViewModel() {
val participants = sharedViewModel.chatRoomParticipants.value
if (participants != null && participants.size > 0) {
val list = arrayListOf<GroupInfoParticipantData>()
for (address in participants) {
val exists = viewModel.participants.value?.find {
it.participant.address.weakEqual(address)
}
if (exists != null) {
list.add(exists)
} else {
list.add(
GroupInfoParticipantData(
GroupChatRoomMember(address, false, hasLimeX3DHCapability = viewModel.isEncrypted.value == true)
)
)
}
}
viewModel.participants.value = list
}
if (sharedViewModel.chatRoomSubject.isNotEmpty()) {
viewModel.subject.value = sharedViewModel.chatRoomSubject
sharedViewModel.chatRoomSubject = ""
}
}
private fun showMeAdminStateChanged(isMeAdmin: Boolean) {
meAdminStatusChangedDialog?.dismiss()
val message = if (isMeAdmin) {
getString(R.string.chat_room_group_info_you_are_now_admin)
} else {
getString(R.string.chat_room_group_info_you_are_no_longer_admin)
}
val dialogViewModel = DialogViewModel(message)
val dialog = DialogUtils.getDialog(requireContext(), dialogViewModel)
dialogViewModel.showOkButton({
dialog.dismiss()
})
dialog.show()
meAdminStatusChangedDialog = dialog
}
private fun goToChatRoom(chatRoom: ChatRoom, created: Boolean) {
sharedViewModel.selectedChatRoom.value = chatRoom
navigateToChatRoom(AppUtils.createBundleWithSharedTextAndFiles(sharedViewModel), created)
}
}

View file

@ -1,99 +0,0 @@
/*
* Copyright (c) 2010-2020 Belledonne Communications SARL.
*
* This file is part of linphone-android
* (see https://www.linphone.org).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.linphone.activities.main.chat.fragments
import android.os.Bundle
import android.view.View
import androidx.lifecycle.ViewModelProvider
import androidx.navigation.fragment.findNavController
import androidx.recyclerview.widget.LinearLayoutManager
import org.linphone.R
import org.linphone.activities.main.chat.adapters.ImdnAdapter
import org.linphone.activities.main.chat.viewmodels.ImdnViewModel
import org.linphone.activities.main.chat.viewmodels.ImdnViewModelFactory
import org.linphone.activities.main.fragments.SecureFragment
import org.linphone.core.tools.Log
import org.linphone.databinding.ChatRoomImdnFragmentBinding
import org.linphone.utils.AppUtils
import org.linphone.utils.RecyclerViewHeaderDecoration
class ImdnFragment : SecureFragment<ChatRoomImdnFragmentBinding>() {
private lateinit var viewModel: ImdnViewModel
private lateinit var adapter: ImdnAdapter
override fun getLayoutId(): Int {
return R.layout.chat_room_imdn_fragment
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding.lifecycleOwner = viewLifecycleOwner
val chatRoom = sharedViewModel.selectedChatRoom.value
if (chatRoom == null) {
Log.e("[IMDN] Chat room is null, aborting!")
findNavController().navigateUp()
return
}
isSecure = chatRoom.currentParams.isEncryptionEnabled
if (arguments != null) {
val messageId = arguments?.getString("MessageId")
val message = if (messageId != null) chatRoom.findMessage(messageId) else null
if (message != null) {
Log.i("[IMDN] Found message $message with id $messageId")
viewModel = ViewModelProvider(
this,
ImdnViewModelFactory(message)
)[ImdnViewModel::class.java]
binding.viewModel = viewModel
} else {
Log.e("[IMDN] Couldn't find message with id $messageId in chat room $chatRoom")
findNavController().popBackStack()
return
}
} else {
Log.e("[IMDN] Couldn't find message id in intent arguments")
findNavController().popBackStack()
return
}
adapter = ImdnAdapter(viewLifecycleOwner)
binding.participantsList.adapter = adapter
val layoutManager = LinearLayoutManager(requireContext())
binding.participantsList.layoutManager = layoutManager
// Divider between items
binding.participantsList.addItemDecoration(AppUtils.getDividerDecoration(requireContext(), layoutManager))
// Displays state header
val headerItemDecoration = RecyclerViewHeaderDecoration(requireContext(), adapter)
binding.participantsList.addItemDecoration(headerItemDecoration)
viewModel.participants.observe(
viewLifecycleOwner
) {
adapter.submitList(it)
}
}
}

View file

@ -1,399 +0,0 @@
/*
* Copyright (c) 2010-2020 Belledonne Communications SARL.
*
* This file is part of linphone-android
* (see https://www.linphone.org).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.linphone.activities.main.chat.fragments
import android.app.Dialog
import android.content.res.Configuration
import android.os.Bundle
import android.view.View
import androidx.core.content.ContextCompat
import androidx.lifecycle.ViewModelProvider
import androidx.navigation.fragment.NavHostFragment
import androidx.recyclerview.widget.ItemTouchHelper
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import androidx.slidingpanelayout.widget.SlidingPaneLayout
import com.google.android.material.transition.MaterialSharedAxis
import org.linphone.LinphoneApplication.Companion.coreContext
import org.linphone.LinphoneApplication.Companion.corePreferences
import org.linphone.R
import org.linphone.activities.GenericActivity
import org.linphone.activities.clearDisplayedChatRoom
import org.linphone.activities.main.MainActivity
import org.linphone.activities.main.chat.adapters.ChatRoomsListAdapter
import org.linphone.activities.main.chat.viewmodels.ChatRoomsListViewModel
import org.linphone.activities.main.fragments.MasterFragment
import org.linphone.activities.main.viewmodels.DialogViewModel
import org.linphone.activities.navigateToChatRoom
import org.linphone.activities.navigateToChatRoomCreation
import org.linphone.core.ChatRoom
import org.linphone.core.Factory
import org.linphone.core.tools.Log
import org.linphone.databinding.ChatRoomMasterFragmentBinding
import org.linphone.utils.*
class MasterChatRoomsFragment : MasterFragment<ChatRoomMasterFragmentBinding, ChatRoomsListAdapter>() {
override val dialogConfirmationMessageBeforeRemoval = R.plurals.chat_room_delete_dialog
private lateinit var listViewModel: ChatRoomsListViewModel
private val observer = object : RecyclerView.AdapterDataObserver() {
override fun onChanged() {
scrollToTop()
}
override fun onItemRangeInserted(positionStart: Int, itemCount: Int) {
if (positionStart == 0 && itemCount == 1) {
scrollToTop()
}
}
override fun onItemRangeMoved(fromPosition: Int, toPosition: Int, itemCount: Int) {
scrollToTop()
}
}
override fun getLayoutId(): Int = R.layout.chat_room_master_fragment
override fun onDestroyView() {
binding.chatList.adapter = null
adapter.unregisterAdapterDataObserver(observer)
super.onDestroyView()
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
useMaterialSharedAxisXForwardAnimation = false
if (corePreferences.enableAnimations) {
val portraitOrientation = resources.configuration.orientation != Configuration.ORIENTATION_LANDSCAPE
val axis = if (portraitOrientation) MaterialSharedAxis.X else MaterialSharedAxis.Y
enterTransition = MaterialSharedAxis(axis, true)
reenterTransition = MaterialSharedAxis(axis, true)
returnTransition = MaterialSharedAxis(axis, false)
exitTransition = MaterialSharedAxis(axis, false)
}
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
isSecure = true
binding.lifecycleOwner = viewLifecycleOwner
listViewModel = requireActivity().run {
ViewModelProvider(this)[ChatRoomsListViewModel::class.java]
}
binding.viewModel = listViewModel
/* Shared view model & sliding pane related */
setUpSlidingPane(binding.slidingPane)
binding.slidingPane.addPanelSlideListener(object : SlidingPaneLayout.PanelSlideListener {
override fun onPanelSlide(panel: View, slideOffset: Float) { }
override fun onPanelOpened(panel: View) { }
override fun onPanelClosed(panel: View) {
// Conversation isn't visible anymore, any new message received in it will trigger a notification
coreContext.notificationsManager.currentlyDisplayedChatRoomAddress = null
}
})
// Chat room loading can take some time, so wait until it is ready before opening the pane
sharedViewModel.chatRoomFragmentOpenedEvent.observe(
viewLifecycleOwner
) {
it.consume {
binding.slidingPane.openPane()
}
}
sharedViewModel.layoutChangedEvent.observe(
viewLifecycleOwner
) {
it.consume {
sharedViewModel.isSlidingPaneSlideable.value = binding.slidingPane.isSlideable
if (binding.slidingPane.isSlideable) {
val navHostFragment =
childFragmentManager.findFragmentById(R.id.chat_nav_container) as NavHostFragment
if (navHostFragment.navController.currentDestination?.id == R.id.emptyChatFragment) {
Log.i("[Chat] Foldable device has been folded, closing side pane with empty fragment")
binding.slidingPane.closePane()
}
}
}
}
sharedViewModel.refreshChatRoomInListEvent.observe(
viewLifecycleOwner
) {
it.consume {
val chatRoom = sharedViewModel.selectedChatRoom.value
if (chatRoom != null) {
listViewModel.notifyChatRoomUpdate(chatRoom)
}
}
}
/* End of shared view model & sliding pane related */
_adapter = ChatRoomsListAdapter(listSelectionViewModel, viewLifecycleOwner)
// SubmitList is done on a background thread
// We need this adapter data observer to know when to scroll
adapter.registerAdapterDataObserver(observer)
binding.chatList.setHasFixedSize(true)
binding.chatList.adapter = adapter
val layoutManager = LinearLayoutManager(requireContext())
binding.chatList.layoutManager = layoutManager
// Swipe action
val swipeConfiguration = RecyclerViewSwipeConfiguration()
val white = ContextCompat.getColor(requireContext(), R.color.white_color)
swipeConfiguration.rightToLeftAction = RecyclerViewSwipeConfiguration.Action(
requireContext().getString(R.string.dialog_delete),
white,
ContextCompat.getColor(requireContext(), R.color.red_color)
)
swipeConfiguration.leftToRightAction = RecyclerViewSwipeConfiguration.Action(
requireContext().getString(R.string.received_chat_notification_mark_as_read_label),
white,
ContextCompat.getColor(requireContext(), R.color.imdn_read_color)
)
val swipeListener = object : RecyclerViewSwipeListener {
override fun onLeftToRightSwipe(viewHolder: RecyclerView.ViewHolder) {
val index = viewHolder.bindingAdapterPosition
if (index < 0 || index >= adapter.currentList.size) {
Log.e("[Chat] Index is out of bound, can't mark chat room as read")
} else {
val chatRoom = adapter.currentList[viewHolder.bindingAdapterPosition]
chatRoom.markAsRead()
adapter.notifyItemChanged(viewHolder.bindingAdapterPosition)
}
}
override fun onRightToLeftSwipe(viewHolder: RecyclerView.ViewHolder) {
val viewModel = DialogViewModel(getString(R.string.chat_room_delete_one_dialog))
val dialog: Dialog = DialogUtils.getDialog(requireContext(), viewModel)
val index = viewHolder.bindingAdapterPosition
if (index < 0 || index >= adapter.currentList.size) {
Log.e("[Chat] Index is out of bound, can't delete chat room")
} else {
viewModel.showCancelButton {
adapter.notifyItemChanged(viewHolder.bindingAdapterPosition)
dialog.dismiss()
}
viewModel.showDeleteButton(
{
val deletedChatRoom =
adapter.currentList[index]
listViewModel.deleteChatRoom(deletedChatRoom)
if (!binding.slidingPane.isSlideable &&
deletedChatRoom == sharedViewModel.selectedChatRoom.value
) {
Log.i("[Chat] Currently displayed chat room has been deleted, removing detail fragment")
clearDisplayedChatRoom()
}
dialog.dismiss()
},
getString(R.string.dialog_delete)
)
dialog.show()
}
}
}
RecyclerViewSwipeUtils(ItemTouchHelper.LEFT or ItemTouchHelper.RIGHT, swipeConfiguration, swipeListener)
.attachToRecyclerView(binding.chatList)
// Divider between items
binding.chatList.addItemDecoration(AppUtils.getDividerDecoration(requireContext(), layoutManager))
listViewModel.chatRooms.observe(
viewLifecycleOwner
) { chatRooms ->
adapter.submitList(chatRooms)
}
listViewModel.chatRoomIndexUpdatedEvent.observe(
viewLifecycleOwner
) {
it.consume { index ->
adapter.notifyItemChanged(index)
}
}
adapter.selectedChatRoomEvent.observe(
viewLifecycleOwner
) {
it.consume { chatRoom ->
if ((requireActivity() as GenericActivity).isDestructionPending) {
Log.w("[Chat] Activity is pending destruction, don't start navigating now!")
sharedViewModel.destructionPendingChatRoom = chatRoom
} else {
if (chatRoom.peerAddress.asStringUriOnly() == coreContext.notificationsManager.currentlyDisplayedChatRoomAddress) {
if (!binding.slidingPane.isOpen) {
Log.w("[Chat] Chat room is displayed but sliding pane is closed...")
if (!binding.slidingPane.openPane()) {
Log.e("[Chat] Tried to open pane to workaround already displayed chat room issue, failed!")
}
} else {
Log.w("[Chat] This chat room is already displayed!")
}
} else {
sharedViewModel.selectedChatRoom.value = chatRoom
navigateToChatRoom(
AppUtils.createBundleWithSharedTextAndFiles(
sharedViewModel
)
)
}
}
}
}
binding.setEditClickListener {
listSelectionViewModel.isEditionEnabled.value = true
}
binding.setCancelForwardClickListener {
sharedViewModel.messageToForwardEvent.value?.consume {
Log.i("[Chat] Cancelling message forward")
}
sharedViewModel.isPendingMessageForward.value = false
}
binding.setCancelSharingClickListener {
Log.i("[Chat] Cancelling text/files sharing")
sharedViewModel.textToShare.value = ""
sharedViewModel.filesToShare.value = arrayListOf()
listViewModel.fileSharingPending.value = false
listViewModel.textSharingPending.value = false
}
binding.setNewOneToOneChatRoomClickListener {
sharedViewModel.chatRoomParticipants.value = arrayListOf()
navigateToChatRoomCreation(false, binding.slidingPane)
}
binding.setNewGroupChatRoomClickListener {
sharedViewModel.selectedGroupChatRoom.value = null
sharedViewModel.chatRoomParticipants.value = arrayListOf()
navigateToChatRoomCreation(true, binding.slidingPane)
}
val pendingDestructionChatRoom = sharedViewModel.destructionPendingChatRoom
if (pendingDestructionChatRoom != null) {
Log.w("[Chat] Found pending chat room from before activity was recreated")
sharedViewModel.destructionPendingChatRoom = null
sharedViewModel.selectedChatRoom.value = pendingDestructionChatRoom
navigateToChatRoom(AppUtils.createBundleWithSharedTextAndFiles(sharedViewModel))
}
val localSipUri = arguments?.getString("LocalSipUri")
val remoteSipUri = arguments?.getString("RemoteSipUri")
if (localSipUri != null && remoteSipUri != null) {
Log.i("[Chat] Found local [$localSipUri] & remote [$remoteSipUri] addresses in arguments")
arguments?.clear()
val localAddress = Factory.instance().createAddress(localSipUri)
val remoteSipAddress = Factory.instance().createAddress(remoteSipUri)
val chatRoom = coreContext.core.searchChatRoom(null, localAddress, remoteSipAddress, arrayOfNulls(0))
if (chatRoom != null) {
Log.i("[Chat] Found matching chat room $chatRoom")
adapter.selectedChatRoomEvent.value = Event(chatRoom)
}
} else {
sharedViewModel.textToShare.observe(
viewLifecycleOwner
) {
if (it.isNotEmpty()) {
Log.i("[Chat] Found text to share")
listViewModel.textSharingPending.value = true
clearDisplayedChatRoom()
} else {
if (sharedViewModel.filesToShare.value.isNullOrEmpty()) {
listViewModel.textSharingPending.value = false
}
}
}
sharedViewModel.filesToShare.observe(
viewLifecycleOwner
) {
if (it.isNotEmpty()) {
Log.i("[Chat] Found ${it.size} files to share")
listViewModel.fileSharingPending.value = true
clearDisplayedChatRoom()
} else {
if (sharedViewModel.textToShare.value.isNullOrEmpty()) {
listViewModel.fileSharingPending.value = false
}
}
}
sharedViewModel.isPendingMessageForward.observe(
viewLifecycleOwner
) {
listViewModel.forwardPending.value = it
adapter.forwardPending(it)
if (it) {
Log.i("[Chat] Found chat message to transfer")
}
}
listViewModel.onMessageToNotifyEvent.observe(
viewLifecycleOwner
) {
it.consume { messageResourceId ->
(activity as MainActivity).showSnackBar(messageResourceId)
}
}
}
}
override fun onResume() {
super.onResume()
listViewModel.groupChatAvailable.value = LinphoneUtils.isGroupChatAvailable()
}
override fun deleteItems(indexesOfItemToDelete: ArrayList<Int>) {
val list = ArrayList<ChatRoom>()
var closeSlidingPane = false
for (index in indexesOfItemToDelete) {
val chatRoom = adapter.currentList[index]
list.add(chatRoom)
if (chatRoom == sharedViewModel.selectedChatRoom.value) {
closeSlidingPane = true
}
}
listViewModel.deleteChatRooms(list)
if (!binding.slidingPane.isSlideable && closeSlidingPane) {
Log.i("[Chat] Currently displayed chat room has been deleted, removing detail fragment")
clearDisplayedChatRoom()
}
}
private fun scrollToTop() {
binding.chatList.scrollToPosition(0)
}
}

View file

@ -1,524 +0,0 @@
/*
* Copyright (c) 2010-2020 Belledonne Communications SARL.
*
* This file is part of linphone-android
* (see https://www.linphone.org).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.linphone.activities.main.chat.viewmodels
import android.view.inputmethod.EditorInfo
import android.widget.Toast
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import androidx.media.AudioFocusRequestCompat
import java.io.File
import java.text.SimpleDateFormat
import java.util.*
import kotlin.collections.ArrayList
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import org.linphone.LinphoneApplication.Companion.coreContext
import org.linphone.LinphoneApplication.Companion.corePreferences
import org.linphone.R
import org.linphone.activities.main.chat.data.ChatMessageAttachmentData
import org.linphone.activities.main.chat.data.ChatMessageData
import org.linphone.compatibility.Compatibility
import org.linphone.core.*
import org.linphone.core.tools.Log
import org.linphone.utils.AppUtils
import org.linphone.utils.Event
import org.linphone.utils.FileUtils
import org.linphone.utils.PermissionHelper
class ChatMessageSendingViewModelFactory(private val chatRoom: ChatRoom) :
ViewModelProvider.NewInstanceFactory() {
@Suppress("UNCHECKED_CAST")
override fun <T : ViewModel> create(modelClass: Class<T>): T {
return ChatMessageSendingViewModel(chatRoom) as T
}
}
class ChatMessageSendingViewModel(private val chatRoom: ChatRoom) : ViewModel() {
var temporaryFileUploadPath: File? = null
val attachments = MutableLiveData<ArrayList<ChatMessageAttachmentData>>()
val attachFileEnabled = MutableLiveData<Boolean>()
val sendMessageEnabled = MutableLiveData<Boolean>()
val isReadOnly = MutableLiveData<Boolean>()
var textToSend = MutableLiveData<String>()
val isPendingAnswer = MutableLiveData<Boolean>()
var pendingChatMessageToReplyTo = MutableLiveData<ChatMessageData>()
val requestRecordAudioPermissionEvent: MutableLiveData<Event<Boolean>> by lazy {
MutableLiveData<Event<Boolean>>()
}
val messageSentEvent: MutableLiveData<Event<Boolean>> by lazy {
MutableLiveData<Event<Boolean>>()
}
val voiceRecordingProgressBarMax = 10000
val isPendingVoiceRecord = MutableLiveData<Boolean>()
val isVoiceRecording = MutableLiveData<Boolean>()
val voiceRecordingDuration = MutableLiveData<Int>()
val formattedDuration = MutableLiveData<String>()
val isPlayingVoiceRecording = MutableLiveData<Boolean>()
val voiceRecordPlayingPosition = MutableLiveData<Int>()
val imeFlags: Int = if (chatRoom.hasCapability(ChatRoomCapabilities.Encrypted.toInt())) {
// IME_FLAG_NO_PERSONALIZED_LEARNING is only available on Android 8 and newer
Compatibility.getImeFlagsForSecureChatRoom()
} else {
EditorInfo.IME_FLAG_NO_EXTRACT_UI
}
private val recorder: Recorder
private var voiceRecordAudioFocusRequest: AudioFocusRequestCompat? = null
private lateinit var voiceRecordingPlayer: Player
private val playerListener = PlayerListener {
Log.i("[Chat Message Sending] End of file reached")
stopVoiceRecordPlayer()
}
private val chatRoomListener: ChatRoomListenerStub = object : ChatRoomListenerStub() {
override fun onStateChanged(chatRoom: ChatRoom, state: ChatRoom.State) {
updateChatRoomReadOnlyState()
}
override fun onConferenceJoined(chatRoom: ChatRoom, eventLog: EventLog) {
updateChatRoomReadOnlyState()
}
override fun onConferenceLeft(chatRoom: ChatRoom, eventLog: EventLog) {
updateChatRoomReadOnlyState()
}
}
private val scope = CoroutineScope(Dispatchers.IO + SupervisorJob())
init {
chatRoom.addListener(chatRoomListener)
attachments.value = arrayListOf()
attachFileEnabled.value = true
sendMessageEnabled.value = false
updateChatRoomReadOnlyState()
val recorderParams = coreContext.core.createRecorderParams()
if (corePreferences.voiceMessagesFormatMkv) {
recorderParams.fileFormat = RecorderFileFormat.Mkv
} else {
recorderParams.fileFormat = RecorderFileFormat.Wav
}
recorder = coreContext.core.createRecorder(recorderParams)
}
override fun onCleared() {
pendingChatMessageToReplyTo.value?.destroy()
if (recorder.state != RecorderState.Closed) {
recorder.close()
}
if (this::voiceRecordingPlayer.isInitialized) {
stopVoiceRecordPlayer()
voiceRecordingPlayer.removeListener(playerListener)
}
chatRoom.removeListener(chatRoomListener)
scope.cancel()
super.onCleared()
}
fun onTextToSendChanged(value: String) {
sendMessageEnabled.value = value.trim().isNotEmpty() || attachments.value?.isNotEmpty() == true || isPendingVoiceRecord.value == true
if (value.isNotEmpty()) {
if (attachFileEnabled.value == true && !corePreferences.allowMultipleFilesAndTextInSameMessage) {
attachFileEnabled.value = false
}
chatRoom.compose()
} else {
if (!corePreferences.allowMultipleFilesAndTextInSameMessage) {
attachFileEnabled.value = attachments.value?.isEmpty() ?: true
}
}
}
fun addAttachment(path: String) {
val list = arrayListOf<ChatMessageAttachmentData>()
list.addAll(attachments.value.orEmpty())
list.add(
ChatMessageAttachmentData(path) {
removeAttachment(it)
}
)
attachments.value = list
sendMessageEnabled.value = textToSend.value.orEmpty().trim().isNotEmpty() || list.isNotEmpty() || isPendingVoiceRecord.value == true
if (!corePreferences.allowMultipleFilesAndTextInSameMessage) {
attachFileEnabled.value = false
}
}
private fun removeAttachment(attachment: ChatMessageAttachmentData) {
val list = arrayListOf<ChatMessageAttachmentData>()
list.addAll(attachments.value.orEmpty())
list.remove(attachment)
attachments.value = list
sendMessageEnabled.value = textToSend.value.orEmpty().trim().isNotEmpty() || list.isNotEmpty() || isPendingVoiceRecord.value == true
if (!corePreferences.allowMultipleFilesAndTextInSameMessage) {
attachFileEnabled.value = list.isEmpty()
}
}
fun sendMessage() {
if (!isPlayerClosed()) {
stopVoiceRecordPlayer()
}
val pendingMessageToReplyTo = pendingChatMessageToReplyTo.value
val message: ChatMessage = if (isPendingAnswer.value == true && pendingMessageToReplyTo != null)
chatRoom.createReplyMessage(pendingMessageToReplyTo.chatMessage)
else
chatRoom.createEmptyMessage()
val isBasicChatRoom: Boolean = chatRoom.hasCapability(ChatRoomCapabilities.Basic.toInt())
var voiceRecord = false
if (isPendingVoiceRecord.value == true && recorder.file != null) {
val content = recorder.createContent()
if (content != null) {
Log.i("[Chat Message Sending] Voice recording content created, file name is ${content.name} and duration is ${content.fileDuration}")
message.addContent(content)
voiceRecord = true
} else {
Log.e("[Chat Message Sending] Voice recording content couldn't be created!")
}
isPendingVoiceRecord.value = false
isVoiceRecording.value = false
}
val toSend = textToSend.value.orEmpty().trim()
if (toSend.isNotEmpty()) {
if (voiceRecord && isBasicChatRoom) {
val textMessage: ChatMessage = chatRoom.createMessageFromUtf8(toSend)
textMessage.send()
} else {
message.addUtf8TextContent(toSend)
}
}
var fileContent = false
for (attachment in attachments.value.orEmpty()) {
val content = Factory.instance().createContent()
if (attachment.isImage) {
content.type = "image"
} else {
content.type = "file"
}
content.subtype = FileUtils.getExtensionFromFileName(attachment.fileName)
content.name = attachment.fileName
content.filePath = attachment.path // Let the file body handler take care of the upload
// Do not send file in the same message as the text in a BasicChatRoom
// and don't send multiple files in the same message if setting says so
if (isBasicChatRoom or (corePreferences.preventMoreThanOneFilePerMessage and (fileContent or voiceRecord))) {
val fileMessage: ChatMessage = chatRoom.createFileTransferMessage(content)
fileMessage.send()
} else {
message.addFileContent(content)
fileContent = true
}
}
if (message.contents.isNotEmpty()) {
message.send()
}
cancelReply()
attachments.value = arrayListOf()
textToSend.value = ""
messageSentEvent.value = Event(true)
}
fun transferMessage(chatMessage: ChatMessage) {
val message = chatRoom.createForwardMessage(chatMessage)
message.send()
}
fun cancelReply() {
pendingChatMessageToReplyTo.value?.destroy()
isPendingAnswer.value = false
}
private fun tickerFlowRecording() = flow {
while (recorder.state == RecorderState.Running) {
emit(Unit)
delay(100)
}
}
private fun tickerFlowPlaying() = flow {
while (voiceRecordingPlayer.state == Player.State.Playing) {
emit(Unit)
delay(100)
}
}
fun toggleVoiceRecording() {
if (corePreferences.holdToRecordVoiceMessage) {
// Disables click listener just in case, touch listener will be used instead
return
}
if (isVoiceRecording.value == true) {
stopVoiceRecording()
} else {
startVoiceRecording()
}
}
fun startVoiceRecording() {
if (!PermissionHelper.get().hasRecordAudioPermission()) {
requestRecordAudioPermissionEvent.value = Event(true)
return
}
if (voiceRecordAudioFocusRequest == null) {
voiceRecordAudioFocusRequest = AppUtils.acquireAudioFocusForVoiceRecordingOrPlayback(
coreContext.context
)
}
when (recorder.state) {
RecorderState.Running -> Log.w("[Chat Message Sending] Recorder is already recording")
RecorderState.Paused -> {
Log.w("[Chat Message Sending] Recorder isn't closed, resuming recording")
recorder.start()
}
RecorderState.Closed -> {
val extension = when (recorder.params.fileFormat) {
RecorderFileFormat.Mkv -> "mkv"
else -> "wav"
}
val tempFileName = "voice-recording-${System.currentTimeMillis()}.$extension"
val file = FileUtils.getFileStoragePath(tempFileName)
Log.w("[Chat Message Sending] Recorder is closed, starting recording in ${file.absoluteFile}")
recorder.open(file.absolutePath)
recorder.start()
}
else -> {}
}
val duration = recorder.duration
voiceRecordingDuration.value = duration
formattedDuration.value = SimpleDateFormat("mm:ss", Locale.getDefault()).format(duration) // duration is in ms
isPendingVoiceRecord.value = true
isVoiceRecording.value = true
sendMessageEnabled.value = true
tickerFlowRecording().onEach {
val duration = recorder.duration
voiceRecordingDuration.postValue(recorder.duration % voiceRecordingProgressBarMax)
formattedDuration.postValue(SimpleDateFormat("mm:ss", Locale.getDefault()).format(duration)) // duration is in ms
if (duration >= corePreferences.voiceRecordingMaxDuration) {
withContext(Dispatchers.Main) {
Log.w("[Chat Message Sending] Max duration for voice recording exceeded (${corePreferences.voiceRecordingMaxDuration}ms), stopping.")
stopVoiceRecording()
}
}
}.launchIn(scope)
}
fun cancelVoiceRecording() {
if (recorder.state != RecorderState.Closed) {
Log.i("[Chat Message Sending] Closing voice recorder")
recorder.close()
val path = recorder.file
if (path != null) {
Log.i("[Chat Message Sending] Deleting voice recording file: $path")
FileUtils.deleteFile(path)
}
}
val request = voiceRecordAudioFocusRequest
if (request != null) {
AppUtils.releaseAudioFocusForVoiceRecordingOrPlayback(coreContext.context, request)
voiceRecordAudioFocusRequest = null
}
isPendingVoiceRecord.value = false
isVoiceRecording.value = false
sendMessageEnabled.value = textToSend.value.orEmpty().trim().isNotEmpty() == true || attachments.value?.isNotEmpty() == true
if (!isPlayerClosed()) {
stopVoiceRecordPlayer()
}
}
fun stopVoiceRecording() {
if (recorder.state == RecorderState.Running) {
Log.i("[Chat Message Sending] Pausing / closing voice recorder")
recorder.pause()
recorder.close()
voiceRecordingDuration.value = recorder.duration
}
val request = voiceRecordAudioFocusRequest
if (request != null) {
AppUtils.releaseAudioFocusForVoiceRecordingOrPlayback(coreContext.context, request)
voiceRecordAudioFocusRequest = null
}
isVoiceRecording.value = false
if (corePreferences.sendVoiceRecordingRightAway) {
Log.i("[Chat Message Sending] Sending voice recording right away")
sendMessage()
}
}
fun playRecordedMessage() {
if (isPlayerClosed()) {
Log.w("[Chat Message Sending] Player closed, let's open it first")
initVoiceRecordPlayer()
}
if (AppUtils.isMediaVolumeLow(coreContext.context)) {
Toast.makeText(coreContext.context, R.string.chat_message_voice_recording_playback_low_volume, Toast.LENGTH_LONG).show()
}
if (voiceRecordAudioFocusRequest == null) {
voiceRecordAudioFocusRequest = AppUtils.acquireAudioFocusForVoiceRecordingOrPlayback(
coreContext.context
)
}
voiceRecordingPlayer.start()
isPlayingVoiceRecording.value = true
tickerFlowPlaying().onEach {
voiceRecordPlayingPosition.postValue(voiceRecordingPlayer.currentPosition)
}.launchIn(scope)
}
fun pauseRecordedMessage() {
Log.i("[Chat Message Sending] Pausing voice record")
if (!isPlayerClosed()) {
voiceRecordingPlayer.pause()
}
val request = voiceRecordAudioFocusRequest
if (request != null) {
AppUtils.releaseAudioFocusForVoiceRecordingOrPlayback(coreContext.context, request)
voiceRecordAudioFocusRequest = null
}
isPlayingVoiceRecording.value = false
}
private fun initVoiceRecordPlayer() {
Log.i("[Chat Message Sending] Creating player for voice record")
// In case no headphones/headset is connected, use speaker sound card to play recordings, otherwise use earpiece
// If none are available, default one will be used
var headphonesCard: String? = null
var speakerCard: String? = null
var earpieceCard: String? = null
for (device in coreContext.core.audioDevices) {
if (device.hasCapability(AudioDevice.Capabilities.CapabilityPlay)) {
when (device.type) {
AudioDevice.Type.Speaker -> {
speakerCard = device.id
}
AudioDevice.Type.Earpiece -> {
earpieceCard = device.id
}
AudioDevice.Type.Headphones, AudioDevice.Type.Headset -> {
headphonesCard = device.id
}
else -> {}
}
}
}
Log.i("[Chat Message Sending] Found headset/headphones sound card [$headphonesCard], speaker sound card [$speakerCard] and earpiece sound card [$earpieceCard]")
val localPlayer = coreContext.core.createLocalPlayer(headphonesCard ?: speakerCard ?: earpieceCard, null, null)
if (localPlayer != null) {
voiceRecordingPlayer = localPlayer
} else {
Log.e("[Chat Message Sending] Couldn't create local player!")
return
}
voiceRecordingPlayer.addListener(playerListener)
val path = recorder.file
if (path != null) {
voiceRecordingPlayer.open(path)
// Update recording duration using player value to ensure proper progress bar animation
voiceRecordingDuration.value = voiceRecordingPlayer.duration
}
}
private fun stopVoiceRecordPlayer() {
if (!isPlayerClosed()) {
Log.i("[Chat Message Sending] Stopping voice record")
voiceRecordingPlayer.pause()
voiceRecordingPlayer.seek(0)
voiceRecordPlayingPosition.value = 0
voiceRecordingPlayer.close()
}
val request = voiceRecordAudioFocusRequest
if (request != null) {
AppUtils.releaseAudioFocusForVoiceRecordingOrPlayback(coreContext.context, request)
voiceRecordAudioFocusRequest = null
}
isPlayingVoiceRecording.value = false
}
private fun isPlayerClosed(): Boolean {
return !this::voiceRecordingPlayer.isInitialized || voiceRecordingPlayer.state == Player.State.Closed
}
private fun updateChatRoomReadOnlyState() {
isReadOnly.value = chatRoom.isReadOnly || (chatRoom.hasCapability(ChatRoomCapabilities.Conference.toInt()) && chatRoom.participants.isEmpty())
}
}

View file

@ -1,254 +0,0 @@
/*
* Copyright (c) 2010-2020 Belledonne Communications SARL.
*
* This file is part of linphone-android
* (see https://www.linphone.org).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.linphone.activities.main.chat.viewmodels
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import java.util.*
import kotlin.math.max
import org.linphone.activities.main.chat.data.EventLogData
import org.linphone.core.*
import org.linphone.core.tools.Log
import org.linphone.utils.Event
import org.linphone.utils.LinphoneUtils
import org.linphone.utils.PermissionHelper
class ChatMessagesListViewModelFactory(private val chatRoom: ChatRoom) :
ViewModelProvider.NewInstanceFactory() {
@Suppress("UNCHECKED_CAST")
override fun <T : ViewModel> create(modelClass: Class<T>): T {
return ChatMessagesListViewModel(chatRoom) as T
}
}
class ChatMessagesListViewModel(private val chatRoom: ChatRoom) : ViewModel() {
companion object {
private const val MESSAGES_PER_PAGE = 20
}
val events = MutableLiveData<ArrayList<EventLogData>>()
val messageUpdatedEvent: MutableLiveData<Event<Int>> by lazy {
MutableLiveData<Event<Int>>()
}
val requestWriteExternalStoragePermissionEvent: MutableLiveData<Event<Boolean>> by lazy {
MutableLiveData<Event<Boolean>>()
}
private val chatRoomListener: ChatRoomListenerStub = object : ChatRoomListenerStub() {
override fun onChatMessagesReceived(chatRoom: ChatRoom, eventLogs: Array<out EventLog>) {
for (eventLog in eventLogs) {
addChatMessageEventLog(eventLog)
}
}
override fun onChatMessageSending(chatRoom: ChatRoom, eventLog: EventLog) {
val position = events.value.orEmpty().size
if (eventLog.type == EventLog.Type.ConferenceChatMessage) {
val chatMessage = eventLog.chatMessage
chatMessage ?: return
chatMessage.userData = position
}
addEvent(eventLog)
}
override fun onSecurityEvent(chatRoom: ChatRoom, eventLog: EventLog) {
addEvent(eventLog)
}
override fun onParticipantAdded(chatRoom: ChatRoom, eventLog: EventLog) {
addEvent(eventLog)
}
override fun onParticipantRemoved(chatRoom: ChatRoom, eventLog: EventLog) {
addEvent(eventLog)
}
override fun onParticipantAdminStatusChanged(chatRoom: ChatRoom, eventLog: EventLog) {
addEvent(eventLog)
}
override fun onSubjectChanged(chatRoom: ChatRoom, eventLog: EventLog) {
addEvent(eventLog)
}
override fun onConferenceJoined(chatRoom: ChatRoom, eventLog: EventLog) {
if (!chatRoom.hasCapability(ChatRoomCapabilities.OneToOne.toInt())) {
addEvent(eventLog)
}
}
override fun onConferenceLeft(chatRoom: ChatRoom, eventLog: EventLog) {
if (!chatRoom.hasCapability(ChatRoomCapabilities.OneToOne.toInt())) {
addEvent(eventLog)
}
}
override fun onEphemeralMessageDeleted(chatRoom: ChatRoom, eventLog: EventLog) {
Log.i("[Chat Messages] An ephemeral chat message has expired, removing it from event list")
deleteEvent(eventLog)
}
override fun onEphemeralEvent(chatRoom: ChatRoom, eventLog: EventLog) {
addEvent(eventLog)
}
}
init {
chatRoom.addListener(chatRoomListener)
events.value = getEvents()
}
override fun onCleared() {
events.value.orEmpty().forEach(EventLogData::destroy)
chatRoom.removeListener(chatRoomListener)
super.onCleared()
}
fun resendMessage(chatMessage: ChatMessage) {
val position: Int = chatMessage.userData as Int
chatMessage.send()
messageUpdatedEvent.value = Event(position)
}
fun deleteMessage(chatMessage: ChatMessage) {
LinphoneUtils.deleteFilesAttachedToChatMessage(chatMessage)
chatRoom.deleteMessage(chatMessage)
events.value.orEmpty().forEach(EventLogData::destroy)
events.value = getEvents()
}
fun deleteEventLogs(listToDelete: ArrayList<EventLogData>) {
for (eventLog in listToDelete) {
LinphoneUtils.deleteFilesAttachedToEventLog(eventLog.eventLog)
eventLog.eventLog.deleteFromDatabase()
}
events.value.orEmpty().forEach(EventLogData::destroy)
events.value = getEvents()
}
fun loadMoreData(totalItemsCount: Int) {
Log.i("[Chat Messages] Load more data, current total is $totalItemsCount")
val maxSize: Int = chatRoom.historyEventsSize
if (totalItemsCount < maxSize) {
var upperBound: Int = totalItemsCount + MESSAGES_PER_PAGE
if (upperBound > maxSize) {
upperBound = maxSize
}
val history: Array<EventLog> = chatRoom.getHistoryRangeEvents(totalItemsCount, upperBound)
val list = arrayListOf<EventLogData>()
for (eventLog in history) {
list.add(EventLogData(eventLog))
}
list.addAll(events.value.orEmpty())
events.value = list
}
}
private fun addEvent(eventLog: EventLog) {
val list = arrayListOf<EventLogData>()
list.addAll(events.value.orEmpty())
val found = list.find { data -> data.eventLog == eventLog }
if (found == null) {
list.add(EventLogData(eventLog))
}
events.value = list
}
private fun getEvents(): ArrayList<EventLogData> {
val list = arrayListOf<EventLogData>()
val unreadCount = chatRoom.unreadMessagesCount
var loadCount = max(MESSAGES_PER_PAGE, unreadCount)
Log.i("[Chat Messages] $unreadCount unread messages in this chat room, loading $loadCount from history")
val history = chatRoom.getHistoryEvents(loadCount)
var messageCount = 0
for (eventLog in history) {
list.add(EventLogData(eventLog))
if (eventLog.type == EventLog.Type.ConferenceChatMessage) {
messageCount += 1
}
}
// Load enough events to have at least all unread messages
while (unreadCount > 0 && messageCount < unreadCount) {
Log.w("[Chat Messages] There is only $messageCount messages in the last $loadCount events, loading $MESSAGES_PER_PAGE more")
val moreHistory = chatRoom.getHistoryRangeEvents(loadCount, loadCount + MESSAGES_PER_PAGE)
loadCount += MESSAGES_PER_PAGE
for (eventLog in moreHistory) {
list.add(EventLogData(eventLog))
if (eventLog.type == EventLog.Type.ConferenceChatMessage) {
messageCount += 1
}
}
}
return list
}
private fun deleteEvent(eventLog: EventLog) {
val chatMessage = eventLog.chatMessage
if (chatMessage != null) {
LinphoneUtils.deleteFilesAttachedToChatMessage(chatMessage)
chatRoom.deleteMessage(chatMessage)
}
events.value.orEmpty().forEach(EventLogData::destroy)
events.value = getEvents()
}
private fun addChatMessageEventLog(eventLog: EventLog) {
if (eventLog.type == EventLog.Type.ConferenceChatMessage) {
val chatMessage = eventLog.chatMessage
chatMessage ?: return
chatMessage.userData = events.value.orEmpty().size
val existingEvent = events.value.orEmpty().find { data ->
data.eventLog == eventLog
}
if (existingEvent != null) {
Log.w("[Chat Messages] Found already present chat message, don't add it it's probably the result of an auto download")
return
}
if (!PermissionHelper.get().hasWriteExternalStoragePermission()) {
for (content in chatMessage.contents) {
if (content.isFileTransfer) {
Log.i("[Chat Messages] Android < 10 detected and WRITE_EXTERNAL_STORAGE permission isn't granted yet")
requestWriteExternalStoragePermissionEvent.value = Event(true)
}
}
}
}
addEvent(eventLog)
}
}

View file

@ -1,139 +0,0 @@
/*
* Copyright (c) 2010-2020 Belledonne Communications SARL.
*
* This file is part of linphone-android
* (see https://www.linphone.org).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.linphone.activities.main.chat.viewmodels
import androidx.lifecycle.MutableLiveData
import org.linphone.LinphoneApplication.Companion.coreContext
import org.linphone.LinphoneApplication.Companion.corePreferences
import org.linphone.R
import org.linphone.contact.ContactsSelectionViewModel
import org.linphone.core.*
import org.linphone.core.tools.Log
import org.linphone.utils.AppUtils
import org.linphone.utils.Event
import org.linphone.utils.LinphoneUtils
class ChatRoomCreationViewModel : ContactsSelectionViewModel() {
val chatRoomCreatedEvent: MutableLiveData<Event<ChatRoom>> by lazy {
MutableLiveData<Event<ChatRoom>>()
}
val createGroupChat = MutableLiveData<Boolean>()
val isEncrypted = MutableLiveData<Boolean>()
val waitForChatRoomCreation = MutableLiveData<Boolean>()
val secureChatAvailable = MutableLiveData<Boolean>()
val secureChatMandatory: Boolean = corePreferences.forceEndToEndEncryptedChat
private val listener = object : ChatRoomListenerStub() {
override fun onStateChanged(room: ChatRoom, state: ChatRoom.State) {
if (state == ChatRoom.State.Created) {
waitForChatRoomCreation.value = false
Log.i("[Chat Room Creation] Chat room created")
chatRoomCreatedEvent.value = Event(room)
} else if (state == ChatRoom.State.CreationFailed) {
Log.e("[Chat Room Creation] Group chat room creation has failed !")
waitForChatRoomCreation.value = false
onMessageToNotifyEvent.value = Event(R.string.chat_room_creation_failed_snack)
}
}
}
init {
createGroupChat.value = false
isEncrypted.value = secureChatMandatory
waitForChatRoomCreation.value = false
secureChatAvailable.value = LinphoneUtils.isEndToEndEncryptedChatAvailable()
}
fun updateEncryption(encrypted: Boolean) {
if (!encrypted && secureChatMandatory) {
Log.w("[Chat Room Creation] Something tries to force plain text chat room even if secureChatMandatory is enabled!")
return
}
isEncrypted.value = encrypted
}
fun createOneToOneChat(searchResult: SearchResult) {
waitForChatRoomCreation.value = true
val defaultAccount = coreContext.core.defaultAccount
var room: ChatRoom?
val address = searchResult.address ?: coreContext.core.interpretUrl(searchResult.phoneNumber ?: "", LinphoneUtils.applyInternationalPrefix())
if (address == null) {
Log.e("[Chat Room Creation] Can't get a valid address from search result $searchResult")
onMessageToNotifyEvent.value = Event(R.string.chat_room_creation_failed_snack)
waitForChatRoomCreation.value = false
return
}
val encrypted = secureChatMandatory || isEncrypted.value == true
val params: ChatRoomParams = coreContext.core.createDefaultChatRoomParams()
params.backend = ChatRoomBackend.Basic
params.isGroupEnabled = false
if (encrypted) {
params.isEncryptionEnabled = true
params.backend = ChatRoomBackend.FlexisipChat
params.ephemeralMode = if (corePreferences.useEphemeralPerDeviceMode)
ChatRoomEphemeralMode.DeviceManaged
else
ChatRoomEphemeralMode.AdminManaged
params.ephemeralLifetime = 0 // Make sure ephemeral is disabled by default
Log.i("[Chat Room Creation] Ephemeral mode is ${params.ephemeralMode}, lifetime is ${params.ephemeralLifetime}")
params.subject = AppUtils.getString(R.string.chat_room_dummy_subject)
}
val participants = arrayOf(address)
val localAddress: Address? = defaultAccount?.params?.identityAddress
room = coreContext.core.searchChatRoom(params, localAddress, null, participants)
if (room == null) {
Log.w("[Chat Room Creation] Couldn't find existing 1-1 chat room with remote ${address.asStringUriOnly()}, encryption=$encrypted and local identity ${localAddress?.asStringUriOnly()}")
room = coreContext.core.createChatRoom(params, localAddress, participants)
if (room != null) {
if (encrypted) {
val state = room.state
if (state == ChatRoom.State.Created) {
Log.i("[Chat Room Creation] Found already created chat room, using it")
chatRoomCreatedEvent.value = Event(room)
waitForChatRoomCreation.value = false
} else {
Log.i("[Chat Room Creation] Chat room creation is pending [$state], waiting for Created state")
room.addListener(listener)
}
} else {
chatRoomCreatedEvent.value = Event(room)
waitForChatRoomCreation.value = false
}
} else {
Log.e("[Chat Room Creation] Couldn't create chat room with remote ${address.asStringUriOnly()} and local identity ${localAddress?.asStringUriOnly()}")
waitForChatRoomCreation.value = false
}
} else {
Log.i("[Chat Room Creation] Found existing 1-1 chat room with remote ${address.asStringUriOnly()}, encryption=$encrypted and local identity ${localAddress?.asStringUriOnly()}")
chatRoomCreatedEvent.value = Event(room)
waitForChatRoomCreation.value = false
}
}
}

View file

@ -1,382 +0,0 @@
/*
* Copyright (c) 2010-2020 Belledonne Communications SARL.
*
* This file is part of linphone-android
* (see https://www.linphone.org).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.linphone.activities.main.chat.viewmodels
import android.animation.ValueAnimator
import android.view.animation.LinearInterpolator
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.viewModelScope
import kotlinx.coroutines.*
import org.linphone.LinphoneApplication.Companion.coreContext
import org.linphone.LinphoneApplication.Companion.corePreferences
import org.linphone.R
import org.linphone.contact.ContactDataInterface
import org.linphone.contact.ContactsUpdatedListenerStub
import org.linphone.core.*
import org.linphone.core.tools.Log
import org.linphone.utils.AppUtils
import org.linphone.utils.LinphoneUtils
class ChatRoomViewModelFactory(private val chatRoom: ChatRoom) :
ViewModelProvider.NewInstanceFactory() {
@Suppress("UNCHECKED_CAST")
override fun <T : ViewModel> create(modelClass: Class<T>): T {
return ChatRoomViewModel(chatRoom) as T
}
}
class ChatRoomViewModel(val chatRoom: ChatRoom) : ViewModel(), ContactDataInterface {
override val contact: MutableLiveData<Friend> = MutableLiveData<Friend>()
override val displayName: MutableLiveData<String> = MutableLiveData<String>()
override val securityLevel: MutableLiveData<ChatRoomSecurityLevel> = MutableLiveData<ChatRoomSecurityLevel>()
override val showGroupChatAvatar: Boolean
get() = conferenceChatRoom && !oneToOneChatRoom
override val coroutineScope: CoroutineScope = viewModelScope
val subject = MutableLiveData<String>()
val participants = MutableLiveData<String>()
val unreadMessagesCount = MutableLiveData<Int>()
val remoteIsComposing = MutableLiveData<Boolean>()
val composingList = MutableLiveData<String>()
val securityLevelIcon = MutableLiveData<Int>()
val securityLevelContentDescription = MutableLiveData<Int>()
val peerSipUri = MutableLiveData<String>()
val ephemeralEnabled = MutableLiveData<Boolean>()
val basicChatRoom: Boolean by lazy {
chatRoom.hasCapability(ChatRoomCapabilities.Basic.toInt())
}
val oneToOneChatRoom: Boolean by lazy {
chatRoom.hasCapability(ChatRoomCapabilities.OneToOne.toInt())
}
private val conferenceChatRoom: Boolean by lazy {
chatRoom.hasCapability(ChatRoomCapabilities.Conference.toInt())
}
val encryptedChatRoom: Boolean by lazy {
chatRoom.hasCapability(ChatRoomCapabilities.Encrypted.toInt())
}
val ephemeralChatRoom: Boolean by lazy {
chatRoom.hasCapability(ChatRoomCapabilities.Ephemeral.toInt())
}
val meAdmin: MutableLiveData<Boolean> by lazy {
MutableLiveData<Boolean>()
}
val isUserScrollingUp = MutableLiveData<Boolean>()
var oneParticipantOneDevice: Boolean = false
var onlyParticipantOnlyDeviceAddress: Address? = null
val chatUnreadCountTranslateY = MutableLiveData<Float>()
val groupCallAvailable: Boolean
get() = LinphoneUtils.isRemoteConferencingAvailable()
private var addressToCall: Address? = null
private val bounceAnimator: ValueAnimator by lazy {
ValueAnimator.ofFloat(AppUtils.getDimension(R.dimen.tabs_fragment_unread_count_bounce_offset), 0f).apply {
addUpdateListener {
val value = it.animatedValue as Float
chatUnreadCountTranslateY.value = value
}
interpolator = LinearInterpolator()
duration = 250
repeatMode = ValueAnimator.REVERSE
repeatCount = ValueAnimator.INFINITE
}
}
private val contactsUpdatedListener = object : ContactsUpdatedListenerStub() {
override fun onContactsUpdated() {
Log.d("[Chat Room] Contacts have changed")
contactLookup()
}
}
private val coreListener: CoreListenerStub = object : CoreListenerStub() {
override fun onChatRoomRead(core: Core, room: ChatRoom) {
if (room == chatRoom) {
updateUnreadMessageCount()
}
}
}
private val chatRoomListener: ChatRoomListenerStub = object : ChatRoomListenerStub() {
override fun onStateChanged(chatRoom: ChatRoom, state: ChatRoom.State) {
Log.i("[Chat Room] $chatRoom state changed: $state")
if (state == ChatRoom.State.Created) {
contactLookup()
updateSecurityIcon()
subject.value = chatRoom.subject
}
}
override fun onSubjectChanged(chatRoom: ChatRoom, eventLog: EventLog) {
subject.value = chatRoom.subject
}
override fun onChatMessagesReceived(chatRoom: ChatRoom, eventLogs: Array<out EventLog>) {
updateUnreadMessageCount()
}
override fun onParticipantAdded(chatRoom: ChatRoom, eventLog: EventLog) {
contactLookup()
updateSecurityIcon()
updateParticipants()
}
override fun onParticipantRemoved(chatRoom: ChatRoom, eventLog: EventLog) {
contactLookup()
updateSecurityIcon()
updateParticipants()
}
override fun onIsComposingReceived(
chatRoom: ChatRoom,
remoteAddr: Address,
isComposing: Boolean
) {
updateRemotesComposing()
}
override fun onConferenceJoined(chatRoom: ChatRoom, eventLog: EventLog) {
contactLookup()
updateSecurityIcon()
subject.value = chatRoom.subject
}
override fun onSecurityEvent(chatRoom: ChatRoom, eventLog: EventLog) {
updateSecurityIcon()
}
override fun onParticipantDeviceAdded(chatRoom: ChatRoom, eventLog: EventLog) {
updateSecurityIcon()
updateParticipants()
}
override fun onParticipantDeviceRemoved(chatRoom: ChatRoom, eventLog: EventLog) {
updateSecurityIcon()
updateParticipants()
}
override fun onEphemeralEvent(chatRoom: ChatRoom, eventLog: EventLog) {
ephemeralEnabled.value = chatRoom.isEphemeralEnabled
}
override fun onParticipantAdminStatusChanged(chatRoom: ChatRoom, eventLog: EventLog) {
meAdmin.value = chatRoom.me?.isAdmin ?: false
}
}
init {
chatRoom.core.addListener(coreListener)
chatRoom.addListener(chatRoomListener)
coreContext.contactsManager.addListener(contactsUpdatedListener)
updateUnreadMessageCount()
subject.value = chatRoom.subject
updateSecurityIcon()
meAdmin.value = chatRoom.me?.isAdmin ?: false
ephemeralEnabled.value = chatRoom.isEphemeralEnabled
contactLookup()
updateParticipants()
updateRemotesComposing()
}
override fun onCleared() {
coreContext.contactsManager.removeListener(contactsUpdatedListener)
chatRoom.removeListener(chatRoomListener)
chatRoom.core.removeListener(coreListener)
if (corePreferences.enableAnimations) bounceAnimator.end()
super.onCleared()
}
fun contactLookup() {
displayName.value = when {
basicChatRoom -> LinphoneUtils.getDisplayName(
chatRoom.peerAddress
)
oneToOneChatRoom -> LinphoneUtils.getDisplayName(
chatRoom.participants.firstOrNull()?.address ?: chatRoom.peerAddress
)
conferenceChatRoom -> chatRoom.subject.orEmpty()
else -> chatRoom.peerAddress.asStringUriOnly()
}
if (oneToOneChatRoom) {
searchMatchingContact()
} else {
getParticipantsNames()
}
}
fun startCall() {
val address = addressToCall
if (address != null) {
coreContext.startCall(address)
}
}
fun startGroupCall() {
val conferenceScheduler = coreContext.core.createConferenceScheduler()
val conferenceInfo = Factory.instance().createConferenceInfo()
val localAddress = chatRoom.localAddress.clone()
localAddress.clean() // Remove GRUU
val addresses = Array(chatRoom.participants.size) {
index ->
chatRoom.participants[index].address
}
val localAccount = coreContext.core.accountList.find {
account ->
account.params.identityAddress?.weakEqual(localAddress) ?: false
}
conferenceInfo.organizer = localAddress
conferenceInfo.subject = subject.value
conferenceInfo.setParticipants(addresses)
conferenceScheduler.account = localAccount
// Will trigger the conference creation/update automatically
conferenceScheduler.info = conferenceInfo
}
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)
}
fun getRemoteAddress(): Address? {
return if (basicChatRoom) {
chatRoom.peerAddress
} else {
if (chatRoom.participants.isNotEmpty()) {
chatRoom.participants[0].address
} else {
Log.e("[Chat Room] ${chatRoom.peerAddress} doesn't have any participant (state ${chatRoom.state})!")
null
}
}
}
private fun searchMatchingContact() {
val remoteAddress = getRemoteAddress()
if (remoteAddress != null) {
contact.value = coreContext.contactsManager.findContactByAddress(remoteAddress)
}
}
private fun getParticipantsNames() {
if (oneToOneChatRoom) return
var participantsList = ""
var index = 0
for (participant in chatRoom.participants) {
val contact = coreContext.contactsManager.findContactByAddress(participant.address)
participantsList += contact?.name ?: LinphoneUtils.getDisplayName(participant.address)
index++
if (index != chatRoom.nbParticipants) participantsList += ", "
}
participants.value = participantsList
}
private fun updateSecurityIcon() {
val level = chatRoom.securityLevel
securityLevel.value = level
securityLevelIcon.value = when (level) {
ChatRoomSecurityLevel.Safe -> R.drawable.security_2_indicator
ChatRoomSecurityLevel.Encrypted -> R.drawable.security_1_indicator
else -> R.drawable.security_alert_indicator
}
securityLevelContentDescription.value = when (level) {
ChatRoomSecurityLevel.Safe -> R.string.content_description_security_level_safe
ChatRoomSecurityLevel.Encrypted -> R.string.content_description_security_level_encrypted
else -> R.string.content_description_security_level_unsafe
}
}
private fun updateRemotesComposing() {
val isComposing = chatRoom.isRemoteComposing
remoteIsComposing.value = isComposing
if (!isComposing) return
var composing = ""
for (address in chatRoom.composingAddresses) {
val contact = coreContext.contactsManager.findContactByAddress(address)
composing += if (composing.isNotEmpty()) ", " else ""
composing += contact?.name ?: LinphoneUtils.getDisplayName(address)
}
composingList.value = AppUtils.getStringWithPlural(R.plurals.chat_room_remote_composing, chatRoom.composingAddresses.size, composing)
}
private fun updateParticipants() {
val participants = chatRoom.participants
peerSipUri.value = if (oneToOneChatRoom && !basicChatRoom) {
participants.firstOrNull()?.address?.asStringUriOnly()
?: chatRoom.peerAddress.asStringUriOnly()
} else {
chatRoom.peerAddress.asStringUriOnly()
}
oneParticipantOneDevice = oneToOneChatRoom &&
chatRoom.me?.devices?.size == 1 &&
participants.firstOrNull()?.devices?.size == 1
addressToCall = if (basicChatRoom)
chatRoom.peerAddress
else
participants.firstOrNull()?.address
onlyParticipantOnlyDeviceAddress = participants.firstOrNull()?.devices?.firstOrNull()?.address
}
private fun updateUnreadMessageCount() {
val count = chatRoom.unreadMessagesCount
unreadMessagesCount.value = count
if (count > 0 && corePreferences.enableAnimations) bounceAnimator.start()
else if (count == 0 && bounceAnimator.isStarted) bounceAnimator.end()
}
}

View file

@ -1,176 +0,0 @@
/*
* Copyright (c) 2010-2020 Belledonne Communications SARL.
*
* This file is part of linphone-android
* (see https://www.linphone.org).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.linphone.activities.main.chat.viewmodels
import androidx.lifecycle.MutableLiveData
import org.linphone.LinphoneApplication.Companion.coreContext
import org.linphone.R
import org.linphone.activities.main.viewmodels.MessageNotifierViewModel
import org.linphone.compatibility.Compatibility
import org.linphone.contact.ContactsUpdatedListenerStub
import org.linphone.core.*
import org.linphone.core.tools.Log
import org.linphone.utils.Event
import org.linphone.utils.LinphoneUtils
class ChatRoomsListViewModel : MessageNotifierViewModel() {
val chatRooms = MutableLiveData<ArrayList<ChatRoom>>()
val fileSharingPending = MutableLiveData<Boolean>()
val textSharingPending = MutableLiveData<Boolean>()
val forwardPending = MutableLiveData<Boolean>()
val groupChatAvailable = MutableLiveData<Boolean>()
val chatRoomIndexUpdatedEvent: MutableLiveData<Event<Int>> by lazy {
MutableLiveData<Event<Int>>()
}
private val listener: CoreListenerStub = object : CoreListenerStub() {
override fun onChatRoomStateChanged(core: Core, chatRoom: ChatRoom, state: ChatRoom.State) {
if (state == ChatRoom.State.Created) {
updateChatRooms()
} else if (state == ChatRoom.State.TerminationFailed) {
Log.e("[Chat Rooms] Group chat room removal for address ${chatRoom.peerAddress.asStringUriOnly()} has failed !")
onMessageToNotifyEvent.value = Event(R.string.chat_room_removal_failed_snack)
}
}
override fun onMessageSent(core: Core, chatRoom: ChatRoom, message: ChatMessage) {
onChatRoomMessageEvent(chatRoom)
}
override fun onMessagesReceived(
core: Core,
chatRoom: ChatRoom,
messages: Array<out ChatMessage>
) {
onChatRoomMessageEvent(chatRoom)
}
override fun onChatRoomRead(core: Core, chatRoom: ChatRoom) {
notifyChatRoomUpdate(chatRoom)
}
override fun onChatRoomEphemeralMessageDeleted(core: Core, chatRoom: ChatRoom) {
notifyChatRoomUpdate(chatRoom)
}
override fun onChatRoomSubjectChanged(core: Core, chatRoom: ChatRoom) {
notifyChatRoomUpdate(chatRoom)
}
}
private val chatRoomListener = object : ChatRoomListenerStub() {
override fun onStateChanged(chatRoom: ChatRoom, newState: ChatRoom.State) {
if (newState == ChatRoom.State.Deleted) {
updateChatRooms()
}
}
}
private val contactsListener = object : ContactsUpdatedListenerStub() {
override fun onContactsUpdated() {
updateChatRooms()
}
}
private var chatRoomsToDeleteCount = 0
init {
groupChatAvailable.value = LinphoneUtils.isGroupChatAvailable()
updateChatRooms()
coreContext.core.addListener(listener)
coreContext.contactsManager.addListener(contactsListener)
}
override fun onCleared() {
coreContext.contactsManager.removeListener(contactsListener)
coreContext.core.removeListener(listener)
super.onCleared()
}
fun deleteChatRoom(chatRoom: ChatRoom?) {
for (eventLog in chatRoom?.getHistoryMessageEvents(0).orEmpty()) {
LinphoneUtils.deleteFilesAttachedToEventLog(eventLog)
}
chatRoomsToDeleteCount = 1
if (chatRoom != null) {
coreContext.notificationsManager.dismissChatNotification(chatRoom)
Compatibility.removeChatRoomShortcut(coreContext.context, chatRoom)
chatRoom.addListener(chatRoomListener)
coreContext.core.deleteChatRoom(chatRoom)
}
}
fun deleteChatRooms(chatRooms: ArrayList<ChatRoom>) {
chatRoomsToDeleteCount = chatRooms.size
for (chatRoom in chatRooms) {
for (eventLog in chatRoom.getHistoryMessageEvents(0)) {
LinphoneUtils.deleteFilesAttachedToEventLog(eventLog)
}
coreContext.notificationsManager.dismissChatNotification(chatRoom)
Compatibility.removeChatRoomShortcut(coreContext.context, chatRoom)
chatRoom.addListener(chatRoomListener)
chatRoom.core.deleteChatRoom(chatRoom)
}
}
fun updateChatRooms() {
val list = arrayListOf<ChatRoom>()
list.addAll(coreContext.core.chatRooms)
chatRooms.value = list
}
fun notifyChatRoomUpdate(chatRoom: ChatRoom) {
val index = findChatRoomIndex(chatRoom)
if (index == -1) updateChatRooms()
else chatRoomIndexUpdatedEvent.value = Event(index)
}
private fun reorderChatRooms() {
val list = arrayListOf<ChatRoom>()
list.addAll(chatRooms.value.orEmpty())
list.sortByDescending { chatRoom -> chatRoom.lastUpdateTime }
chatRooms.value = list
}
private fun findChatRoomIndex(chatRoom: ChatRoom): Int {
for ((index, cr) in chatRooms.value.orEmpty().withIndex()) {
if (LinphoneUtils.areChatRoomsTheSame(cr, chatRoom)) {
return index
}
}
return -1
}
private fun onChatRoomMessageEvent(chatRoom: ChatRoom) {
when (findChatRoomIndex(chatRoom)) {
-1 -> updateChatRooms()
0 -> chatRoomIndexUpdatedEvent.value = Event(0)
else -> reorderChatRooms()
}
}
}

View file

@ -1,83 +0,0 @@
/*
* Copyright (c) 2010-2020 Belledonne Communications SARL.
*
* This file is part of linphone-android
* (see https://www.linphone.org).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.linphone.activities.main.chat.viewmodels
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import org.linphone.activities.main.chat.data.DevicesListGroupData
import org.linphone.core.ChatRoom
import org.linphone.core.ChatRoomListenerStub
import org.linphone.core.EventLog
class DevicesListViewModelFactory(private val chatRoom: ChatRoom) :
ViewModelProvider.NewInstanceFactory() {
@Suppress("UNCHECKED_CAST")
override fun <T : ViewModel> create(modelClass: Class<T>): T {
return DevicesListViewModel(chatRoom) as T
}
}
class DevicesListViewModel(private val chatRoom: ChatRoom) : ViewModel() {
val participants = MutableLiveData<ArrayList<DevicesListGroupData>>()
private val listener = object : ChatRoomListenerStub() {
override fun onParticipantDeviceAdded(chatRoom: ChatRoom, eventLog: EventLog) {
updateParticipants()
}
override fun onParticipantDeviceRemoved(chatRoom: ChatRoom, eventLog: EventLog) {
updateParticipants()
}
override fun onParticipantAdded(chatRoom: ChatRoom, eventLog: EventLog) {
updateParticipants()
}
override fun onParticipantRemoved(chatRoom: ChatRoom, eventLog: EventLog) {
updateParticipants()
}
}
init {
chatRoom.addListener(listener)
}
override fun onCleared() {
participants.value.orEmpty().forEach(DevicesListGroupData::destroy)
chatRoom.removeListener(listener)
super.onCleared()
}
fun updateParticipants() {
participants.value.orEmpty().forEach(DevicesListGroupData::destroy)
val list = arrayListOf<DevicesListGroupData>()
val me = chatRoom.me
if (me != null) list.add(DevicesListGroupData(me))
for (participant in chatRoom.participants) {
list.add(DevicesListGroupData(participant))
}
participants.value = list
}
}

View file

@ -1,88 +0,0 @@
/*
* Copyright (c) 2010-2020 Belledonne Communications SARL.
*
* This file is part of linphone-android
* (see https://www.linphone.org).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.linphone.activities.main.chat.viewmodels
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import org.linphone.R
import org.linphone.activities.main.chat.data.DurationItemClicked
import org.linphone.activities.main.chat.data.EphemeralDurationData
import org.linphone.core.ChatRoom
import org.linphone.core.tools.Log
class EphemeralViewModelFactory(private val chatRoom: ChatRoom) :
ViewModelProvider.NewInstanceFactory() {
@Suppress("UNCHECKED_CAST")
override fun <T : ViewModel> create(modelClass: Class<T>): T {
return EphemeralViewModel(chatRoom) as T
}
}
class EphemeralViewModel(private val chatRoom: ChatRoom) : ViewModel() {
val durationsList = MutableLiveData<ArrayList<EphemeralDurationData>>()
var currentSelectedDuration: Long = 0
private val listener = object : DurationItemClicked {
override fun onDurationValueChanged(duration: Long) {
currentSelectedDuration = duration
computeEphemeralDurationValues()
}
}
init {
Log.i("[Ephemeral Messages] Current lifetime is ${chatRoom.ephemeralLifetime}, ephemeral enabled? ${chatRoom.isEphemeralEnabled}")
currentSelectedDuration = if (chatRoom.isEphemeralEnabled) chatRoom.ephemeralLifetime else 0
computeEphemeralDurationValues()
}
fun updateChatRoomEphemeralDuration() {
Log.i("[Ephemeral Messages] Selected value is $currentSelectedDuration")
if (currentSelectedDuration > 0) {
if (chatRoom.ephemeralLifetime != currentSelectedDuration) {
Log.i("[Ephemeral Messages] Setting new lifetime for ephemeral messages to $currentSelectedDuration")
chatRoom.ephemeralLifetime = currentSelectedDuration
} else {
Log.i("[Ephemeral Messages] Configured lifetime for ephemeral messages was already $currentSelectedDuration")
}
if (!chatRoom.isEphemeralEnabled) {
Log.i("[Ephemeral Messages] Ephemeral messages were disabled, enable them")
chatRoom.isEphemeralEnabled = true
}
} else if (chatRoom.isEphemeralEnabled) {
Log.i("[Ephemeral Messages] Ephemeral messages were enabled, disable them")
chatRoom.isEphemeralEnabled = false
}
}
private fun computeEphemeralDurationValues() {
val list = arrayListOf<EphemeralDurationData>()
list.add(EphemeralDurationData(R.string.chat_room_ephemeral_message_disabled, currentSelectedDuration, 0, listener))
list.add(EphemeralDurationData(R.string.chat_room_ephemeral_message_one_minute, currentSelectedDuration, 60, listener))
list.add(EphemeralDurationData(R.string.chat_room_ephemeral_message_one_hour, currentSelectedDuration, 3600, listener))
list.add(EphemeralDurationData(R.string.chat_room_ephemeral_message_one_day, currentSelectedDuration, 86400, listener))
list.add(EphemeralDurationData(R.string.chat_room_ephemeral_message_three_days, currentSelectedDuration, 259200, listener))
list.add(EphemeralDurationData(R.string.chat_room_ephemeral_message_one_week, currentSelectedDuration, 604800, listener))
durationsList.value = list
}
}

View file

@ -1,234 +0,0 @@
/*
* Copyright (c) 2010-2020 Belledonne Communications SARL.
*
* This file is part of linphone-android
* (see https://www.linphone.org).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.linphone.activities.main.chat.viewmodels
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import org.linphone.LinphoneApplication.Companion.coreContext
import org.linphone.LinphoneApplication.Companion.corePreferences
import org.linphone.R
import org.linphone.activities.main.chat.GroupChatRoomMember
import org.linphone.activities.main.chat.data.GroupInfoParticipantData
import org.linphone.activities.main.viewmodels.MessageNotifierViewModel
import org.linphone.core.*
import org.linphone.core.tools.Log
import org.linphone.utils.Event
class GroupInfoViewModelFactory(private val chatRoom: ChatRoom?) :
ViewModelProvider.NewInstanceFactory() {
@Suppress("UNCHECKED_CAST")
override fun <T : ViewModel> create(modelClass: Class<T>): T {
return GroupInfoViewModel(chatRoom) as T
}
}
class GroupInfoViewModel(val chatRoom: ChatRoom?) : MessageNotifierViewModel() {
val createdChatRoomEvent = MutableLiveData<Event<ChatRoom>>()
val updatedChatRoomEvent = MutableLiveData<Event<ChatRoom>>()
val subject = MutableLiveData<String>()
val participants = MutableLiveData<ArrayList<GroupInfoParticipantData>>()
val isEncrypted = MutableLiveData<Boolean>()
val isMeAdmin = MutableLiveData<Boolean>()
val canLeaveGroup = MutableLiveData<Boolean>()
val waitForChatRoomCreation = MutableLiveData<Boolean>()
val meAdminChangedEvent: MutableLiveData<Event<Boolean>> by lazy {
MutableLiveData<Event<Boolean>>()
}
private val listener = object : ChatRoomListenerStub() {
override fun onStateChanged(chatRoom: ChatRoom, state: ChatRoom.State) {
if (state == ChatRoom.State.Created) {
waitForChatRoomCreation.value = false
createdChatRoomEvent.value = Event(chatRoom) // To trigger going to the chat room
} else if (state == ChatRoom.State.CreationFailed) {
Log.e("[Chat Room Group Info] Group chat room creation has failed !")
waitForChatRoomCreation.value = false
onMessageToNotifyEvent.value = Event(R.string.chat_room_creation_failed_snack)
}
}
override fun onSubjectChanged(chatRoom: ChatRoom, eventLog: EventLog) {
subject.value = chatRoom.subject
}
override fun onParticipantAdded(chatRoom: ChatRoom, eventLog: EventLog) {
updateParticipants()
}
override fun onParticipantRemoved(chatRoom: ChatRoom, eventLog: EventLog) {
updateParticipants()
}
override fun onParticipantAdminStatusChanged(chatRoom: ChatRoom, eventLog: EventLog) {
val admin = chatRoom.me?.isAdmin ?: false
if (admin != isMeAdmin.value) {
isMeAdmin.value = admin
meAdminChangedEvent.value = Event(admin)
}
updateParticipants()
}
}
init {
subject.value = chatRoom?.subject
isMeAdmin.value = chatRoom == null || (chatRoom.me?.isAdmin == true && !chatRoom.isReadOnly)
canLeaveGroup.value = chatRoom != null && !chatRoom.isReadOnly
isEncrypted.value = corePreferences.forceEndToEndEncryptedChat || chatRoom?.hasCapability(ChatRoomCapabilities.Encrypted.toInt()) == true
if (chatRoom != null) updateParticipants()
chatRoom?.addListener(listener)
waitForChatRoomCreation.value = false
}
override fun onCleared() {
participants.value.orEmpty().forEach(GroupInfoParticipantData::destroy)
chatRoom?.removeListener(listener)
super.onCleared()
}
fun createChatRoom() {
waitForChatRoomCreation.value = true
val params: ChatRoomParams = coreContext.core.createDefaultChatRoomParams()
params.isEncryptionEnabled = corePreferences.forceEndToEndEncryptedChat || isEncrypted.value == true
params.isGroupEnabled = true
if (params.isEncryptionEnabled) {
params.ephemeralMode = if (corePreferences.useEphemeralPerDeviceMode)
ChatRoomEphemeralMode.DeviceManaged
else
ChatRoomEphemeralMode.AdminManaged
}
params.ephemeralLifetime = 0 // Make sure ephemeral is disabled by default
Log.i("[Chat Room Group Info] Ephemeral mode is ${params.ephemeralMode}, lifetime is ${params.ephemeralLifetime}")
params.subject = subject.value
val addresses = arrayOfNulls<Address>(participants.value.orEmpty().size)
var index = 0
for (participant in participants.value.orEmpty()) {
addresses[index] = participant.participant.address
Log.i("[Chat Room Group Info] Participant ${participant.sipUri} will be added to group")
index += 1
}
val chatRoom: ChatRoom? = coreContext.core.createChatRoom(params, coreContext.core.defaultAccount?.params?.identityAddress, addresses)
chatRoom?.addListener(listener)
if (chatRoom == null) {
Log.e("[Chat Room Group Info] Couldn't create chat room!")
waitForChatRoomCreation.value = false
onMessageToNotifyEvent.value = Event(R.string.chat_room_creation_failed_snack)
}
}
fun updateRoom() {
if (chatRoom != null) {
// Subject
val newSubject = subject.value.orEmpty()
if (newSubject.isNotEmpty() && newSubject != chatRoom.subject) {
Log.i("[Chat Room Group Info] Subject changed to $newSubject")
chatRoom.subject = newSubject
}
// Removed participants
val participantsToRemove = arrayListOf<Participant>()
for (participant in chatRoom.participants) {
val member = participants.value.orEmpty().find { member ->
participant.address.weakEqual(member.participant.address)
}
if (member == null) {
Log.w("[Chat Room Group Info] Participant ${participant.address.asStringUriOnly()} will be removed from group")
participantsToRemove.add(participant)
}
}
val toRemove = arrayOfNulls<Participant>(participantsToRemove.size)
participantsToRemove.toArray(toRemove)
chatRoom.removeParticipants(toRemove)
// Added participants & new admins
val participantsToAdd = arrayListOf<Address>()
for (member in participants.value.orEmpty()) {
val participant = chatRoom.participants.find { participant ->
participant.address.weakEqual(member.participant.address)
}
if (participant != null) {
// Participant found, check if admin status needs to be updated
if (member.participant.isAdmin != participant.isAdmin) {
if (chatRoom.me?.isAdmin == true) {
Log.i("[Chat Room Group Info] Participant ${member.sipUri} will be admin? ${member.isAdmin}")
chatRoom.setParticipantAdminStatus(participant, member.participant.isAdmin)
}
}
} else {
Log.i("[Chat Room Group Info] Participant ${member.sipUri} will be added to group")
participantsToAdd.add(member.participant.address)
}
}
val toAdd = arrayOfNulls<Address>(participantsToAdd.size)
participantsToAdd.toArray(toAdd)
chatRoom.addParticipants(toAdd)
// Go back to chat room
updatedChatRoomEvent.value = Event(chatRoom)
}
}
fun leaveGroup() {
if (chatRoom != null) {
Log.w("[Chat Room Group Info] Leaving group")
chatRoom.leave()
updatedChatRoomEvent.value = Event(chatRoom)
}
}
fun removeParticipant(participant: GroupChatRoomMember) {
val list = arrayListOf<GroupInfoParticipantData>()
for (data in participants.value.orEmpty()) {
if (!data.participant.address.weakEqual(participant.address)) {
list.add(data)
}
}
participants.value = list
}
private fun updateParticipants() {
val list = arrayListOf<GroupInfoParticipantData>()
if (chatRoom != null) {
for (participant in chatRoom.participants) {
list.add(
GroupInfoParticipantData(
GroupChatRoomMember(participant.address, participant.isAdmin, participant.securityLevel, canBeSetAdmin = true)
)
)
}
}
participants.value = list
}
}

View file

@ -1,83 +0,0 @@
/*
* Copyright (c) 2010-2020 Belledonne Communications SARL.
*
* This file is part of linphone-android
* (see https://www.linphone.org).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.linphone.activities.main.chat.viewmodels
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import org.linphone.activities.main.chat.data.ChatMessageData
import org.linphone.activities.main.chat.data.ImdnParticipantData
import org.linphone.core.ChatMessage
import org.linphone.core.ChatMessageListenerStub
import org.linphone.core.ParticipantImdnState
class ImdnViewModelFactory(private val chatMessage: ChatMessage) :
ViewModelProvider.NewInstanceFactory() {
@Suppress("UNCHECKED_CAST")
override fun <T : ViewModel> create(modelClass: Class<T>): T {
return ImdnViewModel(chatMessage) as T
}
}
class ImdnViewModel(private val chatMessage: ChatMessage) : ViewModel() {
val participants = MutableLiveData<ArrayList<ImdnParticipantData>>()
val chatMessageViewModel = ChatMessageData(chatMessage)
private val listener = object : ChatMessageListenerStub() {
override fun onParticipantImdnStateChanged(
message: ChatMessage,
state: ParticipantImdnState
) {
updateParticipantsLists()
}
}
init {
chatMessage.addListener(listener)
updateParticipantsLists()
}
override fun onCleared() {
participants.value.orEmpty().forEach(ImdnParticipantData::destroy)
chatMessage.removeListener(listener)
super.onCleared()
}
private fun updateParticipantsLists() {
val list = arrayListOf<ImdnParticipantData>()
for (participant in chatMessage.getParticipantsByImdnState(ChatMessage.State.Displayed)) {
list.add(ImdnParticipantData(participant))
}
for (participant in chatMessage.getParticipantsByImdnState(ChatMessage.State.DeliveredToUser)) {
list.add(ImdnParticipantData(participant))
}
for (participant in chatMessage.getParticipantsByImdnState(ChatMessage.State.Delivered)) {
list.add(ImdnParticipantData(participant))
}
for (participant in chatMessage.getParticipantsByImdnState(ChatMessage.State.NotDelivered)) {
list.add(ImdnParticipantData(participant))
}
participants.value = list
}
}

View file

@ -1,72 +0,0 @@
/*
* Copyright (c) 2010-2020 Belledonne Communications SARL.
*
* This file is part of linphone-android
* (see https://www.linphone.org).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.linphone.activities.main.chat.views
import android.content.Context
import android.text.Layout
import android.text.method.LinkMovementMethod
import android.util.AttributeSet
import androidx.appcompat.widget.AppCompatTextView
import kotlin.math.ceil
/**
* The purpose of this class is to have a TextView declared with wrap_content as width that won't
* fill it's parent if it is multi line.
*/
class MultiLineWrapContentWidthTextView : AppCompatTextView {
constructor(context: Context) : super(context)
constructor(context: Context, attrs: AttributeSet?) : super(context, attrs)
constructor(
context: Context,
attrs: AttributeSet?,
defStyleAttr: Int
) : super(context, attrs, defStyleAttr)
override fun setText(text: CharSequence?, type: BufferType?) {
super.setText(text, type)
// Required for PatternClickableSpan
movementMethod = LinkMovementMethod.getInstance()
}
override fun onMeasure(widthSpec: Int, heightSpec: Int) {
super.onMeasure(widthSpec, heightSpec)
if (layout != null && layout.lineCount >= 2) {
val maxLineWidth = ceil(getMaxLineWidth(layout)).toInt()
val uselessPaddingWidth = layout.width - maxLineWidth
val width = measuredWidth - uselessPaddingWidth
val height = measuredHeight
setMeasuredDimension(width, height)
}
}
private fun getMaxLineWidth(layout: Layout): Float {
var maxWidth = 0.0f
val lines = layout.lineCount
for (i in 0 until lines) {
if (layout.getLineWidth(i) > maxWidth) {
maxWidth = layout.getLineWidth(i)
}
}
return maxWidth
}
}

View file

@ -1,181 +0,0 @@
/*
* Copyright (c) 2010-2021 Belledonne Communications SARL.
*
* This file is part of linphone-android
* (see https://www.linphone.org).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.linphone.activities.main.conference.adapters
import android.content.Context
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.databinding.DataBindingUtil
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.MutableLiveData
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.RecyclerView
import org.linphone.R
import org.linphone.activities.main.adapters.SelectionListAdapter
import org.linphone.activities.main.conference.data.ScheduledConferenceData
import org.linphone.activities.main.viewmodels.ListTopBarViewModel
import org.linphone.databinding.ConferenceScheduleCellBinding
import org.linphone.databinding.ConferenceScheduleListHeaderBinding
import org.linphone.utils.Event
import org.linphone.utils.HeaderAdapter
import org.linphone.utils.TimestampUtils
class ScheduledConferencesAdapter(
selectionVM: ListTopBarViewModel,
private val viewLifecycleOwner: LifecycleOwner
) : SelectionListAdapter<ScheduledConferenceData, RecyclerView.ViewHolder>(selectionVM, ConferenceInfoDiffCallback()),
HeaderAdapter {
val copyAddressToClipboardEvent: MutableLiveData<Event<String>> by lazy {
MutableLiveData<Event<String>>()
}
val joinConferenceEvent: MutableLiveData<Event<Pair<String, String?>>> by lazy {
MutableLiveData<Event<Pair<String, String?>>>()
}
val editConferenceEvent: MutableLiveData<Event<String>> by lazy {
MutableLiveData<Event<String>>()
}
val deleteConferenceInfoEvent: MutableLiveData<Event<ScheduledConferenceData>> by lazy {
MutableLiveData<Event<ScheduledConferenceData>>()
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ScheduledConferencesAdapter.ViewHolder {
val binding: ConferenceScheduleCellBinding = DataBindingUtil.inflate(
LayoutInflater.from(parent.context),
R.layout.conference_schedule_cell, parent, false
)
return ViewHolder(binding)
}
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
(holder as ScheduledConferencesAdapter.ViewHolder).bind(getItem(position))
}
override fun displayHeaderForPosition(position: Int): Boolean {
if (position >= itemCount) return false
val conferenceInfo = getItem(position)
val previousPosition = position - 1
return if (previousPosition >= 0) {
val previousItem = getItem(previousPosition)
!TimestampUtils.isSameDay(previousItem.conferenceInfo.dateTime, conferenceInfo.conferenceInfo.dateTime)
} else true
}
override fun getHeaderViewForPosition(context: Context, position: Int): View {
val data = getItem(position)
val binding: ConferenceScheduleListHeaderBinding = DataBindingUtil.inflate(
LayoutInflater.from(context),
R.layout.conference_schedule_list_header, null, false
)
binding.title = formatDate(context, data.conferenceInfo.dateTime)
binding.executePendingBindings()
return binding.root
}
private fun formatDate(context: Context, date: Long): String {
if (TimestampUtils.isToday(date)) {
return context.getString(R.string.today)
}
return TimestampUtils.toString(date, onlyDate = true, shortDate = false, hideYear = false)
}
inner class ViewHolder(
val binding: ConferenceScheduleCellBinding
) : RecyclerView.ViewHolder(binding.root) {
fun bind(conferenceData: ScheduledConferenceData) {
with(binding) {
data = conferenceData
lifecycleOwner = viewLifecycleOwner
// This is for item selection through ListTopBarFragment
selectionListViewModel = selectionViewModel
selectionViewModel.isEditionEnabled.observe(
viewLifecycleOwner
) {
position = bindingAdapterPosition
}
setClickListener {
if (selectionViewModel.isEditionEnabled.value == true) {
selectionViewModel.onToggleSelect(bindingAdapterPosition)
} else {
conferenceData.toggleExpand()
}
}
setLongClickListener {
if (selectionViewModel.isEditionEnabled.value == false) {
selectionViewModel.isEditionEnabled.value = true
// Selection will be handled by click listener
true
}
false
}
setCopyAddressClickListener {
val address = conferenceData.getAddressAsString()
if (address.isNotEmpty()) {
copyAddressToClipboardEvent.value = Event(address)
}
}
setJoinConferenceClickListener {
val address = conferenceData.conferenceInfo.uri
if (address != null) {
joinConferenceEvent.value = Event(Pair(address.asStringUriOnly(), conferenceData.conferenceInfo.subject))
}
}
setEditConferenceClickListener {
val address = conferenceData.conferenceInfo.uri
if (address != null) {
editConferenceEvent.value = Event(address.asStringUriOnly())
}
}
setDeleteConferenceClickListener {
deleteConferenceInfoEvent.value = Event(conferenceData)
}
executePendingBindings()
}
}
}
}
private class ConferenceInfoDiffCallback : DiffUtil.ItemCallback<ScheduledConferenceData>() {
override fun areItemsTheSame(
oldItem: ScheduledConferenceData,
newItem: ScheduledConferenceData
): Boolean {
return oldItem.conferenceInfo == newItem.conferenceInfo
}
override fun areContentsTheSame(
oldItem: ScheduledConferenceData,
newItem: ScheduledConferenceData
): Boolean {
return false
}
}

View file

@ -1,33 +0,0 @@
/*
* Copyright (c) 2010-2021 Belledonne Communications SARL.
*
* This file is part of linphone-android
* (see https://www.linphone.org).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.linphone.activities.main.conference.data
import org.linphone.contact.GenericContactData
import org.linphone.core.Address
import org.linphone.utils.LinphoneUtils
class ConferenceSchedulingParticipantData(
private val sipAddress: Address,
val showLimeBadge: Boolean = false,
val showDivider: Boolean = true
) :
GenericContactData(sipAddress) {
val sipUri: String get() = LinphoneUtils.getDisplayableAddress(sipAddress)
}

View file

@ -1,148 +0,0 @@
/*
* Copyright (c) 2010-2021 Belledonne Communications SARL.
*
* This file is part of linphone-android
* (see https://www.linphone.org).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.linphone.activities.main.conference.data
import androidx.lifecycle.MutableLiveData
import java.util.concurrent.TimeUnit
import org.linphone.LinphoneApplication.Companion.coreContext
import org.linphone.R
import org.linphone.core.ConferenceInfo
import org.linphone.core.ConferenceInfo.State
import org.linphone.core.tools.Log
import org.linphone.utils.LinphoneUtils
import org.linphone.utils.TimestampUtils
class ScheduledConferenceData(val conferenceInfo: ConferenceInfo, private val isFinished: Boolean) {
val expanded = MutableLiveData<Boolean>()
val backgroundResId = MutableLiveData<Int>()
val address = MutableLiveData<String>()
val subject = MutableLiveData<String>()
val description = MutableLiveData<String>()
val time = MutableLiveData<String>()
val date = MutableLiveData<String>()
val duration = MutableLiveData<String>()
val organizer = MutableLiveData<String>()
val canEdit = MutableLiveData<Boolean>()
val participantsShort = MutableLiveData<String>()
val participantsExpanded = MutableLiveData<String>()
val showDuration = MutableLiveData<Boolean>()
val isConferenceCancelled = MutableLiveData<Boolean>()
init {
expanded.value = false
address.value = conferenceInfo.uri?.asStringUriOnly()
subject.value = conferenceInfo.subject
description.value = conferenceInfo.description
time.value = TimestampUtils.timeToString(conferenceInfo.dateTime)
date.value = TimestampUtils.toString(conferenceInfo.dateTime, onlyDate = true, shortDate = false, hideYear = false)
isConferenceCancelled.value = conferenceInfo.state == State.Cancelled
val minutes = conferenceInfo.duration
val hours = TimeUnit.MINUTES.toHours(minutes.toLong())
val remainMinutes = minutes - TimeUnit.HOURS.toMinutes(hours).toInt()
duration.value = TimestampUtils.durationToString(hours.toInt(), remainMinutes)
showDuration.value = minutes > 0
val organizerAddress = conferenceInfo.organizer
if (organizerAddress != null) {
val localAccount = coreContext.core.accountList.find { account ->
val address = account.params.identityAddress
address != null && organizerAddress.weakEqual(address)
}
canEdit.value = localAccount != null
val contact = coreContext.contactsManager.findContactByAddress(organizerAddress)
organizer.value = if (contact != null)
contact.name
else
LinphoneUtils.getDisplayName(conferenceInfo.organizer)
} else {
canEdit.value = false
Log.e("[Scheduled Conference] No organizer SIP URI found for: ${conferenceInfo.uri?.asStringUriOnly()}")
}
computeBackgroundResId()
computeParticipantsLists()
}
fun destroy() {}
fun delete() {
Log.w("[Scheduled Conference] Deleting conference info with URI: ${conferenceInfo.uri?.asStringUriOnly()}")
coreContext.core.deleteConferenceInformation(conferenceInfo)
}
fun toggleExpand() {
expanded.value = expanded.value == false
computeBackgroundResId()
}
fun getAddressAsString(): String {
val address = conferenceInfo.uri?.clone()
if (address != null) {
address.displayName = conferenceInfo.subject
return address.asString()
}
return ""
}
private fun computeBackgroundResId() {
backgroundResId.value = if (conferenceInfo.state == State.Cancelled) {
if (expanded.value == true) {
R.drawable.shape_round_red_background_with_orange_border
} else {
R.drawable.shape_round_red_background
}
} else if (isFinished) {
if (expanded.value == true) {
R.drawable.shape_round_dark_gray_background_with_orange_border
} else {
R.drawable.shape_round_dark_gray_background
}
} else {
if (expanded.value == true) {
R.drawable.shape_round_gray_background_with_orange_border
} else {
R.drawable.shape_round_gray_background
}
}
}
private fun computeParticipantsLists() {
var participantsListShort = ""
var participantsListExpanded = ""
for (participant in conferenceInfo.participants) {
val contact = coreContext.contactsManager.findContactByAddress(participant)
val name = if (contact != null) contact.name else LinphoneUtils.getDisplayName(participant)
val address = participant.asStringUriOnly()
participantsListShort += "$name, "
participantsListExpanded += "$name ($address)\n"
}
participantsListShort = participantsListShort.dropLast(2)
participantsListExpanded = participantsListExpanded.dropLast(1)
participantsShort.value = participantsListShort
participantsExpanded.value = participantsListExpanded
}
}

View file

@ -1,119 +0,0 @@
/*
* Copyright (c) 2010-2021 Belledonne Communications SARL.
*
* This file is part of linphone-android
* (see https://www.linphone.org).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.linphone.activities.main.conference.fragments
import android.os.Bundle
import android.text.format.DateFormat.is24HourFormat
import android.view.View
import androidx.navigation.navGraphViewModels
import com.google.android.material.datepicker.CalendarConstraints
import com.google.android.material.datepicker.DateValidatorPointForward
import com.google.android.material.datepicker.MaterialDatePicker
import com.google.android.material.timepicker.MaterialTimePicker
import com.google.android.material.timepicker.TimeFormat
import org.linphone.LinphoneApplication.Companion.coreContext
import org.linphone.R
import org.linphone.activities.GenericFragment
import org.linphone.activities.main.conference.viewmodels.ConferenceSchedulingViewModel
import org.linphone.activities.navigateToParticipantsList
import org.linphone.core.Factory
import org.linphone.core.tools.Log
import org.linphone.databinding.ConferenceSchedulingFragmentBinding
class ConferenceSchedulingFragment : GenericFragment<ConferenceSchedulingFragmentBinding>() {
private val viewModel: ConferenceSchedulingViewModel by navGraphViewModels(R.id.conference_scheduling_nav_graph)
override fun getLayoutId(): Int = R.layout.conference_scheduling_fragment
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding.lifecycleOwner = viewLifecycleOwner
binding.viewModel = viewModel
sharedViewModel.participantsListForNextScheduledMeeting.observe(
viewLifecycleOwner
) {
it.consume { participants ->
Log.i("[Conference Scheduling] Found participants (${participants.size}) to pre-populate for meeting schedule")
viewModel.prePopulateParticipantsList(participants, true)
}
}
sharedViewModel.addressOfConferenceInfoToEdit.observe(
viewLifecycleOwner
) {
it.consume { address ->
val conferenceAddress = Factory.instance().createAddress(address)
if (conferenceAddress != null) {
Log.i("[Conference Scheduling] Trying to edit conference info using address: $address")
val conferenceInfo = coreContext.core.findConferenceInformationFromUri(conferenceAddress)
if (conferenceInfo != null) {
viewModel.populateFromConferenceInfo(conferenceInfo)
} else {
Log.e("[Conference Scheduling] Failed to find ConferenceInfo matching address: $address")
}
} else {
Log.e("[Conference Scheduling] Failed to parse conference address: $address")
}
}
}
binding.setNextClickListener {
navigateToParticipantsList()
}
binding.setDatePickerClickListener {
val constraintsBuilder =
CalendarConstraints.Builder()
.setValidator(DateValidatorPointForward.now())
val picker =
MaterialDatePicker.Builder.datePicker()
.setCalendarConstraints(constraintsBuilder.build())
.setTitleText(R.string.conference_schedule_date)
.setSelection(viewModel.dateTimestamp)
.build()
picker.addOnPositiveButtonClickListener {
val selection = picker.selection
if (selection != null) {
viewModel.setDate(selection)
}
}
picker.show(requireFragmentManager(), "Date picker")
}
binding.setTimePickerClickListener {
val isSystem24Hour = is24HourFormat(requireContext())
val clockFormat = if (isSystem24Hour) TimeFormat.CLOCK_24H else TimeFormat.CLOCK_12H
val picker =
MaterialTimePicker.Builder()
.setTimeFormat(clockFormat)
.setTitleText(R.string.conference_schedule_time)
.setHour(viewModel.hour)
.setMinute(viewModel.minutes)
.build()
picker.addOnPositiveButtonClickListener {
viewModel.setTime(picker.hour, picker.minute)
}
picker.show(requireFragmentManager(), "Time picker")
}
}
}

View file

@ -1,120 +0,0 @@
/*
* Copyright (c) 2010-2021 Belledonne Communications SARL.
*
* This file is part of linphone-android
* (see https://www.linphone.org).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.linphone.activities.main.conference.fragments
import android.content.pm.PackageManager
import android.os.Bundle
import android.view.View
import androidx.navigation.navGraphViewModels
import androidx.recyclerview.widget.LinearLayoutManager
import org.linphone.LinphoneApplication.Companion.coreContext
import org.linphone.LinphoneApplication.Companion.corePreferences
import org.linphone.R
import org.linphone.activities.GenericFragment
import org.linphone.activities.main.conference.viewmodels.ConferenceSchedulingViewModel
import org.linphone.activities.navigateToSummary
import org.linphone.contact.ContactsSelectionAdapter
import org.linphone.core.tools.Log
import org.linphone.databinding.ConferenceSchedulingParticipantsListFragmentBinding
import org.linphone.utils.AppUtils
import org.linphone.utils.PermissionHelper
class ConferenceSchedulingParticipantsListFragment : GenericFragment<ConferenceSchedulingParticipantsListFragmentBinding>() {
private val viewModel: ConferenceSchedulingViewModel by navGraphViewModels(R.id.conference_scheduling_nav_graph)
private lateinit var adapter: ContactsSelectionAdapter
override fun getLayoutId(): Int = R.layout.conference_scheduling_participants_list_fragment
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding.lifecycleOwner = viewLifecycleOwner
binding.viewModel = viewModel
adapter = ContactsSelectionAdapter(viewLifecycleOwner)
adapter.setLimeCapabilityRequired(viewModel.isEncrypted.value == true)
binding.contactsList.adapter = adapter
val layoutManager = LinearLayoutManager(requireContext())
binding.contactsList.layoutManager = layoutManager
// Divider between items
binding.contactsList.addItemDecoration(AppUtils.getDividerDecoration(requireContext(), layoutManager))
binding.setNextClickListener {
navigateToSummary()
}
viewModel.contactsList.observe(
viewLifecycleOwner
) {
adapter.submitList(it)
}
viewModel.sipContactsSelected.observe(
viewLifecycleOwner
) {
viewModel.applyFilter()
}
viewModel.selectedAddresses.observe(
viewLifecycleOwner
) {
adapter.updateSelectedAddresses(it)
}
viewModel.filter.observe(
viewLifecycleOwner
) {
viewModel.applyFilter()
}
adapter.selectedContact.observe(
viewLifecycleOwner
) {
it.consume { searchResult ->
viewModel.toggleSelectionForSearchResult(searchResult)
}
}
if (corePreferences.enableNativeAddressBookIntegration) {
if (!PermissionHelper.get().hasReadContactsPermission()) {
Log.i("[Conference Creation] Asking for READ_CONTACTS permission")
requestPermissions(arrayOf(android.Manifest.permission.READ_CONTACTS), 0)
}
}
}
@Deprecated("Deprecated in Java")
override fun onRequestPermissionsResult(
requestCode: Int,
permissions: Array<out String>,
grantResults: IntArray
) {
if (requestCode == 0) {
val granted = grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED
if (granted) {
Log.i("[Conference Creation] READ_CONTACTS permission granted")
coreContext.fetchContacts()
} else {
Log.w("[Conference Creation] READ_CONTACTS permission denied")
}
}
}
}

View file

@ -1,68 +0,0 @@
/*
* Copyright (c) 2010-2021 Belledonne Communications SARL.
*
* This file is part of linphone-android
* (see https://www.linphone.org).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.linphone.activities.main.conference.fragments
import android.os.Bundle
import android.view.View
import androidx.navigation.navGraphViewModels
import org.linphone.R
import org.linphone.activities.GenericFragment
import org.linphone.activities.main.MainActivity
import org.linphone.activities.main.conference.viewmodels.ConferenceSchedulingViewModel
import org.linphone.activities.navigateToDialer
import org.linphone.activities.navigateToScheduledConferences
import org.linphone.databinding.ConferenceSchedulingSummaryFragmentBinding
class ConferenceSchedulingSummaryFragment : GenericFragment<ConferenceSchedulingSummaryFragmentBinding>() {
private val viewModel: ConferenceSchedulingViewModel by navGraphViewModels(R.id.conference_scheduling_nav_graph)
override fun getLayoutId(): Int = R.layout.conference_scheduling_summary_fragment
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding.lifecycleOwner = viewLifecycleOwner
binding.viewModel = viewModel
viewModel.conferenceCreationCompletedEvent.observe(
viewLifecycleOwner
) {
it.consume {
if (viewModel.scheduleForLater.value == true) {
(requireActivity() as MainActivity).showSnackBar(R.string.conference_schedule_info_created)
navigateToScheduledConferences()
} else {
navigateToDialer()
}
}
}
viewModel.onMessageToNotifyEvent.observe(
viewLifecycleOwner
) {
it.consume { messageId ->
(activity as MainActivity).showSnackBar(messageId)
}
}
viewModel.computeParticipantsData()
}
}

View file

@ -1,195 +0,0 @@
/*
* Copyright (c) 2010-2021 Belledonne Communications SARL.
*
* This file is part of linphone-android
* (see https://www.linphone.org).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.linphone.activities.main.conference.fragments
import android.Manifest
import android.content.pm.PackageManager
import android.os.Bundle
import android.view.View
import androidx.lifecycle.ViewModelProvider
import org.linphone.LinphoneApplication.Companion.coreContext
import org.linphone.R
import org.linphone.activities.GenericFragment
import org.linphone.activities.main.MainActivity
import org.linphone.activities.main.conference.viewmodels.ConferenceWaitingRoomViewModel
import org.linphone.compatibility.Compatibility
import org.linphone.core.tools.Log
import org.linphone.databinding.ConferenceWaitingRoomFragmentBinding
import org.linphone.mediastream.Version
import org.linphone.utils.PermissionHelper
class ConferenceWaitingRoomFragment : GenericFragment<ConferenceWaitingRoomFragmentBinding>() {
private lateinit var viewModel: ConferenceWaitingRoomViewModel
override fun getLayoutId(): Int = R.layout.conference_waiting_room_fragment
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding.lifecycleOwner = viewLifecycleOwner
viewModel = ViewModelProvider(
this
)[ConferenceWaitingRoomViewModel::class.java]
binding.viewModel = viewModel
val conferenceSubject = arguments?.getString("Subject")
viewModel.subject.value = conferenceSubject
viewModel.cancelConferenceJoiningEvent.observe(
viewLifecycleOwner
) {
it.consume {
if (viewModel.joinInProgress.value == true) {
val conferenceUri = arguments?.getString("Address")
val callToCancel = coreContext.core.calls.find { call ->
call.remoteAddress.asStringUriOnly() == conferenceUri
}
if (callToCancel != null) {
Log.i("[Conference Waiting Room] Call to conference server with URI [$conferenceUri] was started, terminate it")
callToCancel.terminate()
} else {
Log.w("[Conference Waiting Room] Call to conference server with URI [$conferenceUri] wasn't found!")
}
}
goBack()
}
}
viewModel.joinConferenceEvent.observe(
viewLifecycleOwner
) {
it.consume { callParams ->
val conferenceUri = arguments?.getString("Address")
if (conferenceUri != null) {
val conferenceAddress = coreContext.core.interpretUrl(conferenceUri, false)
if (conferenceAddress != null) {
Log.i("[Conference Waiting Room] Calling conference SIP URI: ${conferenceAddress.asStringUriOnly()}")
coreContext.startCall(conferenceAddress, callParams)
} else {
Log.e("[Conference Waiting Room] Failed to parse conference SIP URI: $conferenceUri")
}
} else {
Log.e("[Conference Waiting Room] Failed to find conference SIP URI in arguments")
}
}
}
viewModel.askPermissionEvent.observe(
viewLifecycleOwner
) {
it.consume { permission ->
Log.i("[Conference Waiting Room] Asking for $permission permission")
requestPermissions(arrayOf(permission), 0)
}
}
viewModel.leaveWaitingRoomEvent.observe(
viewLifecycleOwner
) {
it.consume {
goBack()
}
}
viewModel.onMessageToNotifyEvent.observe(
viewLifecycleOwner
) {
it.consume { message ->
(activity as MainActivity).showSnackBar(message)
}
}
viewModel.networkNotReachableEvent.observe(
viewLifecycleOwner
) {
it.consume {
(activity as MainActivity).showSnackBar(R.string.call_error_network_unreachable)
}
}
checkPermissions()
}
override fun onResume() {
super.onResume()
coreContext.core.nativePreviewWindowId = binding.localPreviewVideoSurface
coreContext.core.isVideoPreviewEnabled = viewModel.isVideoEnabled.value == true
}
override fun onPause() {
coreContext.core.nativePreviewWindowId = null
super.onPause()
}
private fun checkPermissions() {
val permissionsRequiredList = arrayListOf<String>()
if (!PermissionHelper.get().hasRecordAudioPermission()) {
Log.i("[Conference Waiting Room] Asking for RECORD_AUDIO permission")
permissionsRequiredList.add(Manifest.permission.RECORD_AUDIO)
}
if (!PermissionHelper.get().hasCameraPermission()) {
Log.i("[Conference Waiting Room] Asking for CAMERA permission")
permissionsRequiredList.add(Manifest.permission.CAMERA)
}
if (Version.sdkAboveOrEqual(Version.API31_ANDROID_12) && !PermissionHelper.get().hasBluetoothConnectPermission()) {
Log.i("[Conference Waiting Room] Asking for BLUETOOTH_CONNECT permission")
permissionsRequiredList.add(Compatibility.BLUETOOTH_CONNECT)
}
if (permissionsRequiredList.isNotEmpty()) {
val permissionsRequired = arrayOfNulls<String>(permissionsRequiredList.size)
permissionsRequiredList.toArray(permissionsRequired)
requestPermissions(permissionsRequired, 0)
}
}
@Deprecated("Deprecated in Java")
override fun onRequestPermissionsResult(
requestCode: Int,
permissions: Array<out String>,
grantResults: IntArray
) {
if (requestCode == 0) {
for (i in permissions.indices) {
when (permissions[i]) {
Manifest.permission.RECORD_AUDIO -> if (grantResults[i] == PackageManager.PERMISSION_GRANTED) {
Log.i("[Conference Waiting Room] RECORD_AUDIO permission has been granted")
viewModel.enableMic()
}
Manifest.permission.CAMERA -> if (grantResults[i] == PackageManager.PERMISSION_GRANTED) {
Log.i("[Conference Waiting Room] CAMERA permission has been granted")
coreContext.core.reloadVideoDevices()
viewModel.enableVideo()
}
Compatibility.BLUETOOTH_CONNECT -> if (grantResults[i] == PackageManager.PERMISSION_GRANTED) {
Log.i("[Conference Waiting Room] BLUETOOTH_CONNECT permission has been granted")
}
}
}
}
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
}
}

View file

@ -1,184 +0,0 @@
/*
* Copyright (c) 2010-2021 Belledonne Communications SARL.
*
* This file is part of linphone-android
* (see https://www.linphone.org).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.linphone.activities.main.conference.fragments
import android.app.Dialog
import android.content.ClipData
import android.content.ClipboardManager
import android.content.Context
import android.os.Bundle
import android.view.View
import androidx.core.content.ContextCompat
import androidx.lifecycle.ViewModelProvider
import androidx.recyclerview.widget.ItemTouchHelper
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import org.linphone.R
import org.linphone.activities.main.MainActivity
import org.linphone.activities.main.conference.adapters.ScheduledConferencesAdapter
import org.linphone.activities.main.conference.data.ScheduledConferenceData
import org.linphone.activities.main.conference.viewmodels.ScheduledConferencesViewModel
import org.linphone.activities.main.fragments.MasterFragment
import org.linphone.activities.main.viewmodels.DialogViewModel
import org.linphone.activities.navigateToConferenceScheduling
import org.linphone.activities.navigateToConferenceWaitingRoom
import org.linphone.core.tools.Log
import org.linphone.databinding.ConferencesScheduledFragmentBinding
import org.linphone.utils.*
class ScheduledConferencesFragment : MasterFragment<ConferencesScheduledFragmentBinding, ScheduledConferencesAdapter>() {
override val dialogConfirmationMessageBeforeRemoval = R.plurals.conference_scheduled_delete_dialog
private lateinit var listViewModel: ScheduledConferencesViewModel
override fun getLayoutId(): Int = R.layout.conferences_scheduled_fragment
private var deleteConferenceInfoDialog: Dialog? = null
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding.lifecycleOwner = viewLifecycleOwner
listViewModel = ViewModelProvider(
this
)[ScheduledConferencesViewModel::class.java]
binding.viewModel = listViewModel
_adapter = ScheduledConferencesAdapter(listSelectionViewModel, viewLifecycleOwner)
binding.conferenceInfoList.adapter = adapter
val layoutManager = LinearLayoutManager(requireContext())
binding.conferenceInfoList.layoutManager = layoutManager
// Swipe action
val swipeConfiguration = RecyclerViewSwipeConfiguration()
val white = ContextCompat.getColor(requireContext(), R.color.white_color)
swipeConfiguration.rightToLeftAction = RecyclerViewSwipeConfiguration.Action(
requireContext().getString(R.string.dialog_delete),
white,
ContextCompat.getColor(requireContext(), R.color.red_color)
)
val swipeListener = object : RecyclerViewSwipeListener {
override fun onLeftToRightSwipe(viewHolder: RecyclerView.ViewHolder) {}
override fun onRightToLeftSwipe(viewHolder: RecyclerView.ViewHolder) {
val index = viewHolder.bindingAdapterPosition
if (index < 0 || index >= adapter.currentList.size) {
Log.e("[Scheduled Conferences] Index is out of bound, can't delete conference info")
} else {
val deletedConfInfo = adapter.currentList[index]
showConfInfoDeleteConfirmationDialog(deletedConfInfo, index)
}
}
}
RecyclerViewSwipeUtils(ItemTouchHelper.LEFT, swipeConfiguration, swipeListener)
.attachToRecyclerView(binding.conferenceInfoList)
// Displays date header
val headerItemDecoration = RecyclerViewHeaderDecoration(requireContext(), adapter)
binding.conferenceInfoList.addItemDecoration(headerItemDecoration)
listViewModel.conferences.observe(
viewLifecycleOwner
) {
adapter.submitList(it)
}
adapter.copyAddressToClipboardEvent.observe(
viewLifecycleOwner
) {
it.consume { address ->
val clipboard =
requireContext().getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
val clip = ClipData.newPlainText("Conference address", address)
clipboard.setPrimaryClip(clip)
(activity as MainActivity).showSnackBar(R.string.conference_schedule_address_copied_to_clipboard)
}
}
adapter.joinConferenceEvent.observe(
viewLifecycleOwner
) {
it.consume { pair ->
navigateToConferenceWaitingRoom(pair.first, pair.second)
}
}
adapter.editConferenceEvent.observe(
viewLifecycleOwner
) {
it.consume { address ->
sharedViewModel.addressOfConferenceInfoToEdit.value = Event(address)
navigateToConferenceScheduling()
}
}
adapter.deleteConferenceInfoEvent.observe(
viewLifecycleOwner
) {
it.consume { data ->
showConfInfoDeleteConfirmationDialog(data, -1)
}
}
binding.setNewConferenceClickListener {
navigateToConferenceScheduling()
}
}
override fun deleteItems(indexesOfItemToDelete: ArrayList<Int>) {
val list = ArrayList<ScheduledConferenceData>()
for (index in indexesOfItemToDelete) {
val conferenceData = adapter.currentList[index]
list.add(conferenceData)
}
listViewModel.deleteConferencesInfo(list)
}
private fun showConfInfoDeleteConfirmationDialog(data: ScheduledConferenceData, index: Int) {
val dialogViewModel =
DialogViewModel(AppUtils.getString(R.string.conference_scheduled_delete_one_dialog))
deleteConferenceInfoDialog =
DialogUtils.getVoipDialog(requireContext(), dialogViewModel)
dialogViewModel.showCancelButton(
{
if (index != -1) {
adapter.notifyItemChanged(index)
}
deleteConferenceInfoDialog?.dismiss()
},
getString(R.string.dialog_cancel)
)
dialogViewModel.showDeleteButton(
{
listViewModel.deleteConferenceInfo(data)
deleteConferenceInfoDialog?.dismiss()
(requireActivity() as MainActivity).showSnackBar(R.string.conference_info_removed)
},
getString(R.string.dialog_delete)
)
deleteConferenceInfoDialog?.show()
}
}

View file

@ -1,322 +0,0 @@
/*
* Copyright (c) 2010-2021 Belledonne Communications SARL.
*
* This file is part of linphone-android
* (see https://www.linphone.org).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.linphone.activities.main.conference.viewmodels
import androidx.lifecycle.MediatorLiveData
import androidx.lifecycle.MutableLiveData
import java.util.*
import org.linphone.LinphoneApplication.Companion.coreContext
import org.linphone.R
import org.linphone.activities.main.conference.data.ConferenceSchedulingParticipantData
import org.linphone.activities.main.conference.data.Duration
import org.linphone.activities.main.conference.data.TimeZoneData
import org.linphone.contact.ContactsSelectionViewModel
import org.linphone.core.*
import org.linphone.core.tools.Log
import org.linphone.utils.Event
import org.linphone.utils.LinphoneUtils
import org.linphone.utils.TimestampUtils
class ConferenceSchedulingViewModel : ContactsSelectionViewModel() {
val subject = MutableLiveData<String>()
val description = MutableLiveData<String>()
val scheduleForLater = MutableLiveData<Boolean>()
val isUpdate = MutableLiveData<Boolean>()
val formattedDate = MutableLiveData<String>()
val formattedTime = MutableLiveData<String>()
val isEncrypted = MutableLiveData<Boolean>()
val sendInviteViaChat = MutableLiveData<Boolean>()
val sendInviteViaEmail = MutableLiveData<Boolean>()
val participantsData = MutableLiveData<List<ConferenceSchedulingParticipantData>>()
val address = MutableLiveData<Address>()
val conferenceCreationInProgress = MutableLiveData<Boolean>()
val conferenceCreationCompletedEvent: MutableLiveData<Event<Boolean>> by lazy {
MutableLiveData<Event<Boolean>>()
}
val continueEnabled: MediatorLiveData<Boolean> = MediatorLiveData()
var timeZone = MutableLiveData<TimeZoneData>()
val timeZones: List<TimeZoneData> = computeTimeZonesList()
var duration = MutableLiveData<Duration>()
val durationList: List<Duration> = computeDurationList()
var dateTimestamp: Long = System.currentTimeMillis()
var hour: Int = 0
var minutes: Int = 0
private var confInfo: ConferenceInfo? = null
private val conferenceScheduler = coreContext.core.createConferenceScheduler()
private val listener = object : ConferenceSchedulerListenerStub() {
override fun onStateChanged(
conferenceScheduler: ConferenceScheduler,
state: ConferenceScheduler.State
) {
Log.i("[Conference Creation] Conference scheduler state is $state")
if (state == ConferenceScheduler.State.Ready) {
val conferenceAddress = conferenceScheduler.info?.uri
Log.i("[Conference Creation] Conference info created, address will be ${conferenceAddress?.asStringUriOnly()}")
conferenceAddress ?: return
address.value = conferenceAddress!!
if (scheduleForLater.value == true && sendInviteViaChat.value == true) {
// Send conference info even when conf is not scheduled for later
// as the conference server doesn't invite participants automatically
val chatRoomParams = LinphoneUtils.getConferenceInvitationsChatRoomParams()
conferenceScheduler.sendInvitations(chatRoomParams)
} else {
// Will be done in coreListener
}
} else if (state == ConferenceScheduler.State.Error) {
Log.e("[Conference Creation] Failed to create conference!")
conferenceCreationInProgress.value = false
onMessageToNotifyEvent.value = Event(R.string.conference_creation_failed)
}
}
override fun onInvitationsSent(
conferenceScheduler: ConferenceScheduler,
failedInvitations: Array<out Address>?
) {
conferenceCreationInProgress.value = false
if (failedInvitations?.isNotEmpty() == true) {
for (address in failedInvitations) {
Log.e("[Conference Creation] Conference information wasn't sent to participant ${address.asStringUriOnly()}")
}
onMessageToNotifyEvent.value = Event(R.string.conference_schedule_info_not_sent_to_participant)
} else {
Log.i("[Conference Creation] Conference information successfully sent to all participants")
}
val conferenceAddress = conferenceScheduler.info?.uri
if (conferenceAddress == null) {
Log.e("[Conference Creation] Conference address is null!")
} else {
conferenceCreationCompletedEvent.value = Event(true)
}
}
}
private val coreListener: CoreListenerStub = object : CoreListenerStub() {
override fun onCallStateChanged(
core: Core,
call: Call,
state: Call.State?,
message: String
) {
when (state) {
Call.State.OutgoingProgress -> {
conferenceCreationInProgress.value = false
}
Call.State.End -> {
Log.i("[Conference Creation] Call has ended, leaving waiting room fragment")
conferenceCreationCompletedEvent.value = Event(true)
}
Call.State.Error -> {
Log.w("[Conference Creation] Call has failed, leaving waiting room fragment")
conferenceCreationCompletedEvent.value = Event(true)
}
else -> {}
}
}
}
init {
sipContactsSelected.value = true
subject.value = ""
scheduleForLater.value = false
isUpdate.value = false
isEncrypted.value = false
sendInviteViaChat.value = true
sendInviteViaEmail.value = false
timeZone.value = timeZones.find {
it.id == TimeZone.getDefault().id
}
duration.value = durationList.find {
it.value == 3600
}
continueEnabled.value = false
continueEnabled.addSource(subject) {
continueEnabled.value = allMandatoryFieldsFilled()
}
continueEnabled.addSource(scheduleForLater) {
continueEnabled.value = allMandatoryFieldsFilled()
}
continueEnabled.addSource(formattedDate) {
continueEnabled.value = allMandatoryFieldsFilled()
}
continueEnabled.addSource(formattedTime) {
continueEnabled.value = allMandatoryFieldsFilled()
}
conferenceScheduler.addListener(listener)
coreContext.core.addListener(coreListener)
}
override fun onCleared() {
coreContext.core.removeListener(coreListener)
conferenceScheduler.removeListener(listener)
participantsData.value.orEmpty().forEach(ConferenceSchedulingParticipantData::destroy)
super.onCleared()
}
fun prePopulateParticipantsList(participants: ArrayList<Address>, isSchedule: Boolean) {
selectedAddresses.value = participants
scheduleForLater.value = isSchedule
}
fun populateFromConferenceInfo(conferenceInfo: ConferenceInfo) {
confInfo = conferenceInfo
address.value = conferenceInfo.uri
subject.value = conferenceInfo.subject
description.value = conferenceInfo.description
isUpdate.value = true
val dateTime = conferenceInfo.dateTime
val calendar = Calendar.getInstance()
calendar.timeInMillis = dateTime * 1000
setDate(calendar.timeInMillis)
setTime(calendar.get(Calendar.HOUR_OF_DAY), calendar.get(Calendar.MINUTE))
val conferenceDuration = conferenceInfo.duration
duration.value = durationList.find { it.value == conferenceDuration }
scheduleForLater.value = conferenceDuration > 0
val participantsList = arrayListOf<Address>()
for (participant in conferenceInfo.participants) {
participantsList.add(participant)
}
selectedAddresses.value = participantsList
computeParticipantsData()
}
fun toggleSchedule() {
scheduleForLater.value = scheduleForLater.value == false
}
fun setDate(d: Long) {
dateTimestamp = d
formattedDate.value = TimestampUtils.dateToString(dateTimestamp, false)
}
fun setTime(h: Int, m: Int) {
hour = h
minutes = m
formattedTime.value = TimestampUtils.timeToString(hour, minutes)
}
fun updateEncryption(enable: Boolean) {
isEncrypted.value = enable
}
fun computeParticipantsData() {
participantsData.value.orEmpty().forEach(ConferenceSchedulingParticipantData::destroy)
val list = arrayListOf<ConferenceSchedulingParticipantData>()
for (address in selectedAddresses.value.orEmpty()) {
val data = ConferenceSchedulingParticipantData(address, isEncrypted.value == true)
list.add(data)
}
participantsData.value = list
}
fun createConference() {
val participantsCount = selectedAddresses.value.orEmpty().size
if (participantsCount == 0) {
Log.e("[Conference Creation] Couldn't create conference without any participant!")
return
}
conferenceCreationInProgress.value = true
val core = coreContext.core
val participants = arrayOfNulls<Address>(selectedAddresses.value.orEmpty().size)
selectedAddresses.value?.toArray(participants)
val localAccount = core.defaultAccount
val localAddress = localAccount?.params?.identityAddress
val conferenceInfo = if (isUpdate.value == true) {
confInfo?.clone() ?: Factory.instance().createConferenceInfo()
} else {
Factory.instance().createConferenceInfo()
}
conferenceInfo.organizer = localAddress
conferenceInfo.subject = subject.value
conferenceInfo.description = description.value
conferenceInfo.setParticipants(participants)
if (scheduleForLater.value == true) {
val startTime = getConferenceStartTimestamp()
conferenceInfo.dateTime = startTime
val duration = duration.value?.value ?: 0
conferenceInfo.duration = duration
}
confInfo = conferenceInfo
conferenceScheduler.account = localAccount
// Will trigger the conference creation/update automatically
conferenceScheduler.info = conferenceInfo
}
private fun computeTimeZonesList(): List<TimeZoneData> {
return TimeZone.getAvailableIDs().map { id -> TimeZoneData(TimeZone.getTimeZone(id)) }.toList().sorted()
}
private fun computeDurationList(): List<Duration> {
// Duration value is in minutes as according to conferenceInfo.setDuration() doc
return arrayListOf(Duration(30, "30min"), Duration(60, "1h"), Duration(120, "2h"))
}
private fun allMandatoryFieldsFilled(): Boolean {
return !subject.value.isNullOrEmpty() &&
(
scheduleForLater.value == false ||
(
!formattedDate.value.isNullOrEmpty() &&
!formattedTime.value.isNullOrEmpty()
)
)
}
private fun getConferenceStartTimestamp(): Long {
val calendar = Calendar.getInstance(TimeZone.getTimeZone(timeZone.value?.id ?: TimeZone.getDefault().id))
calendar.timeInMillis = dateTimestamp
calendar.set(Calendar.HOUR_OF_DAY, hour)
calendar.set(Calendar.MINUTE, minutes)
return calendar.timeInMillis / 1000 // Linphone expects a time_t (so in seconds)
}
}

View file

@ -1,407 +0,0 @@
/*
* Copyright (c) 2010-2021 Belledonne Communications SARL.
*
* This file is part of linphone-android
* (see https://www.linphone.org).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.linphone.activities.main.conference.viewmodels
import android.Manifest
import android.animation.ValueAnimator
import androidx.lifecycle.MutableLiveData
import org.linphone.LinphoneApplication.Companion.coreContext
import org.linphone.LinphoneApplication.Companion.corePreferences
import org.linphone.R
import org.linphone.activities.main.viewmodels.MessageNotifierViewModel
import org.linphone.activities.voip.ConferenceDisplayMode
import org.linphone.core.*
import org.linphone.core.tools.Log
import org.linphone.utils.*
import org.linphone.utils.Event
class ConferenceWaitingRoomViewModel : MessageNotifierViewModel() {
val subject = MutableLiveData<String>()
val isMicrophoneMuted = MutableLiveData<Boolean>()
val audioRoutesEnabled = MutableLiveData<Boolean>()
val audioRoutesSelected = MutableLiveData<Boolean>()
val isSpeakerSelected = MutableLiveData<Boolean>()
val isBluetoothHeadsetSelected = MutableLiveData<Boolean>()
val layoutMenuSelected = MutableLiveData<Boolean>()
val selectedLayout = MutableLiveData<ConferenceDisplayMode>()
val isVideoAvailable = MutableLiveData<Boolean>()
val isVideoEnabled = MutableLiveData<Boolean>()
val isSwitchCameraAvailable = MutableLiveData<Boolean>()
val isLowBandwidth = MutableLiveData<Boolean>()
val joinInProgress = MutableLiveData<Boolean>()
val networkReachable = MutableLiveData<Boolean>()
val askPermissionEvent: MutableLiveData<Event<String>> by lazy {
MutableLiveData<Event<String>>()
}
val cancelConferenceJoiningEvent: MutableLiveData<Event<Boolean>> by lazy {
MutableLiveData<Event<Boolean>>()
}
val joinConferenceEvent: MutableLiveData<Event<CallParams>> by lazy {
MutableLiveData<Event<CallParams>>()
}
val leaveWaitingRoomEvent: MutableLiveData<Event<Boolean>> by lazy {
MutableLiveData<Event<Boolean>>()
}
val networkNotReachableEvent: MutableLiveData<Event<Boolean>> by lazy {
MutableLiveData<Event<Boolean>>()
}
val audioRoutesMenuTranslateY = MutableLiveData<Float>()
private val audioRoutesMenuAnimator: ValueAnimator by lazy {
ValueAnimator.ofFloat(AppUtils.getDimension(R.dimen.voip_audio_routes_menu_translate_y), 0f).apply {
addUpdateListener {
val value = it.animatedValue as Float
audioRoutesMenuTranslateY.value = value
}
duration = if (corePreferences.enableAnimations) 500 else 0
}
}
val conferenceLayoutMenuTranslateY = MutableLiveData<Float>()
private val conferenceLayoutMenuAnimator: ValueAnimator by lazy {
ValueAnimator.ofFloat(AppUtils.getDimension(R.dimen.voip_audio_routes_menu_translate_y), 0f).apply {
addUpdateListener {
val value = it.animatedValue as Float
conferenceLayoutMenuTranslateY.value = value
}
duration = if (corePreferences.enableAnimations) 500 else 0
}
}
private val callParams: CallParams = coreContext.core.createCallParams(null)!!
private val listener: CoreListenerStub = object : CoreListenerStub() {
override fun onAudioDevicesListUpdated(core: Core) {
Log.i("[Conference Waiting Room] Audio devices list updated")
onAudioDevicesListUpdated()
}
override fun onCallStateChanged(
core: Core,
call: Call,
state: Call.State?,
message: String
) {
when (state) {
Call.State.End -> {
Log.i("[Conference Waiting Room] Call has ended, leaving waiting room fragment")
leaveWaitingRoomEvent.value = Event(true)
}
Call.State.Error -> {
Log.w("[Conference Waiting Room] Call has failed, leaving waiting room fragment")
leaveWaitingRoomEvent.value = Event(true)
}
else -> {}
}
}
override fun onConferenceStateChanged(
core: Core,
conference: Conference,
state: Conference.State?
) {
if (state == Conference.State.Created) {
Log.i("[Conference Waiting Room] Conference has been created, leaving waiting room fragment")
leaveWaitingRoomEvent.value = Event(true)
}
}
override fun onNetworkReachable(core: Core, reachable: Boolean) {
Log.i("[Conference Waiting Room] Network reachability changed: [$reachable]")
networkReachable.value = reachable
if (!reachable) {
networkNotReachableEvent.value = Event(true)
}
}
}
init {
val core = coreContext.core
core.addListener(listener)
audioRoutesMenuTranslateY.value = AppUtils.getDimension(R.dimen.voip_audio_routes_menu_translate_y)
conferenceLayoutMenuTranslateY.value = AppUtils.getDimension(R.dimen.voip_audio_routes_menu_translate_y)
val reachable = core.isNetworkReachable
networkReachable.value = reachable
if (!reachable) {
networkNotReachableEvent.value = Event(true)
}
callParams.isMicEnabled = PermissionHelper.get().hasRecordAudioPermission() && coreContext.core.isMicEnabled
Log.i("[Conference Waiting Room] Microphone will be ${if (callParams.isMicEnabled) "enabled" else "muted"}")
updateMicState()
callParams.isVideoEnabled = isVideoAvailableInCore()
callParams.videoDirection = if (core.videoActivationPolicy.automaticallyInitiate) MediaDirection.SendRecv else MediaDirection.RecvOnly
updateVideoState()
isLowBandwidth.value = false
if (LinphoneUtils.checkIfNetworkHasLowBandwidth(coreContext.context)) {
Log.w("[Conference Waiting Room] Enabling low bandwidth mode, forcing audio only layout!")
callParams.isLowBandwidthEnabled = true
callParams.isVideoEnabled = false
callParams.videoDirection = MediaDirection.Inactive
isLowBandwidth.value = true
updateVideoState()
onMessageToNotifyEvent.value = Event(R.string.conference_low_bandwidth)
}
layoutMenuSelected.value = false
updateLayout()
if (AudioRouteUtils.isBluetoothAudioRouteAvailable()) {
setBluetoothAudioRoute()
} else if (isVideoAvailableInCore() && isVideoEnabled.value == true) {
setSpeakerAudioRoute()
} else {
setEarpieceAudioRoute()
}
onAudioDevicesListUpdated()
}
override fun onCleared() {
coreContext.core.removeListener(listener)
super.onCleared()
}
fun cancel() {
cancelConferenceJoiningEvent.value = Event(true)
}
fun start() {
// Hide menus
audioRoutesSelected.value = false
layoutMenuSelected.value = false
joinInProgress.value = true
joinConferenceEvent.value = Event(callParams)
}
fun toggleMuteMicrophone() {
if (!PermissionHelper.get().hasRecordAudioPermission()) {
askPermissionEvent.value = Event(Manifest.permission.RECORD_AUDIO)
return
}
callParams.isMicEnabled = !callParams.isMicEnabled
Log.i("[Conference Waiting Room] Microphone will be ${if (callParams.isMicEnabled) "enabled" else "muted"}")
updateMicState()
}
fun enableMic() {
Log.i("[Conference Waiting Room] Microphone will be enabled")
callParams.isMicEnabled = true
updateMicState()
}
fun toggleSpeaker() {
if (isSpeakerSelected.value == true) {
setEarpieceAudioRoute()
} else {
setSpeakerAudioRoute()
}
}
fun toggleAudioRoutesMenu() {
audioRoutesSelected.value = audioRoutesSelected.value != true
if (audioRoutesSelected.value == true) {
audioRoutesMenuAnimator.start()
} else {
audioRoutesMenuAnimator.reverse()
}
}
fun setBluetoothAudioRoute() {
Log.i("[Conference Waiting Room] Set default output audio device to Bluetooth")
callParams.outputAudioDevice = coreContext.core.audioDevices.find {
it.type == AudioDevice.Type.Bluetooth && it.hasCapability(AudioDevice.Capabilities.CapabilityPlay)
}
callParams.inputAudioDevice = coreContext.core.audioDevices.find {
it.type == AudioDevice.Type.Bluetooth && it.hasCapability(AudioDevice.Capabilities.CapabilityRecord)
}
updateAudioRouteState()
}
fun setSpeakerAudioRoute() {
Log.i("[Conference Waiting Room] Set default output audio device to Speaker")
callParams.outputAudioDevice = coreContext.core.audioDevices.find {
it.type == AudioDevice.Type.Speaker && it.hasCapability(AudioDevice.Capabilities.CapabilityPlay)
}
callParams.inputAudioDevice = coreContext.core.audioDevices.find {
it.type == AudioDevice.Type.Microphone && it.hasCapability(AudioDevice.Capabilities.CapabilityRecord)
}
updateAudioRouteState()
}
fun setEarpieceAudioRoute() {
Log.i("[Conference Waiting Room] Set default output audio device to Earpiece")
callParams.outputAudioDevice = coreContext.core.audioDevices.find {
it.type == AudioDevice.Type.Earpiece && it.hasCapability(AudioDevice.Capabilities.CapabilityPlay)
}
callParams.inputAudioDevice = coreContext.core.audioDevices.find {
it.type == AudioDevice.Type.Microphone && it.hasCapability(AudioDevice.Capabilities.CapabilityRecord)
}
updateAudioRouteState()
}
fun toggleLayoutMenu() {
layoutMenuSelected.value = layoutMenuSelected.value != true
if (layoutMenuSelected.value == true) {
conferenceLayoutMenuAnimator.start()
} else {
conferenceLayoutMenuAnimator.reverse()
}
}
fun setMosaicLayout() {
Log.i("[Conference Waiting Room] Set default layout to Mosaic")
callParams.conferenceVideoLayout = ConferenceLayout.Grid
callParams.isVideoEnabled = isVideoAvailableInCore()
updateLayout()
updateVideoState()
layoutMenuSelected.value = false
}
fun setActiveSpeakerLayout() {
Log.i("[Conference Waiting Room] Set default layout to ActiveSpeaker")
callParams.conferenceVideoLayout = ConferenceLayout.ActiveSpeaker
callParams.isVideoEnabled = isVideoAvailableInCore()
updateLayout()
updateVideoState()
layoutMenuSelected.value = false
}
fun setAudioOnlyLayout() {
Log.i("[Conference Waiting Room] Set default layout to AudioOnly, disabling video in call")
callParams.isVideoEnabled = false
updateLayout()
updateVideoState()
layoutMenuSelected.value = false
}
fun toggleVideo() {
if (!PermissionHelper.get().hasCameraPermission()) {
askPermissionEvent.value = Event(Manifest.permission.CAMERA)
return
}
callParams.isVideoEnabled = isVideoAvailableInCore()
callParams.videoDirection = if (callParams.videoDirection == MediaDirection.SendRecv) MediaDirection.RecvOnly else MediaDirection.SendRecv
updateVideoState()
}
fun enableVideo() {
callParams.isVideoEnabled = isVideoAvailableInCore()
callParams.videoDirection = MediaDirection.SendRecv
updateVideoState()
}
fun switchCamera() {
Log.i("[Conference Waiting Room] Switching camera")
coreContext.switchCamera()
}
private fun updateMicState() {
isMicrophoneMuted.value = !callParams.isMicEnabled
}
private fun onAudioDevicesListUpdated() {
val bluetoothDeviceAvailable = AudioRouteUtils.isBluetoothAudioRouteAvailable()
audioRoutesEnabled.value = bluetoothDeviceAvailable
if (!bluetoothDeviceAvailable) {
audioRoutesSelected.value = false
Log.w("[Conference Waiting Room] Bluetooth device no longer available, switching back to default microphone & earpiece/speaker")
if (isBluetoothHeadsetSelected.value == true) {
for (audioDevice in coreContext.core.audioDevices) {
if (isVideoEnabled.value == true) {
if (audioDevice.type == AudioDevice.Type.Speaker) {
callParams.outputAudioDevice = audioDevice
}
} else {
if (audioDevice.type == AudioDevice.Type.Earpiece) {
callParams.outputAudioDevice = audioDevice
}
}
if (audioDevice.type == AudioDevice.Type.Microphone) {
callParams.inputAudioDevice = audioDevice
}
}
}
}
updateAudioRouteState()
}
private fun updateAudioRouteState() {
val outputDeviceType = callParams.outputAudioDevice?.type
isSpeakerSelected.value = outputDeviceType == AudioDevice.Type.Speaker
isBluetoothHeadsetSelected.value = outputDeviceType == AudioDevice.Type.Bluetooth
}
private fun updateLayout() {
if (!callParams.isVideoEnabled) {
selectedLayout.value = ConferenceDisplayMode.AUDIO_ONLY
} else {
selectedLayout.value = when (callParams.conferenceVideoLayout) {
ConferenceLayout.Grid -> ConferenceDisplayMode.GRID
else -> ConferenceDisplayMode.ACTIVE_SPEAKER
}
}
}
private fun updateVideoState() {
isVideoAvailable.value = callParams.isVideoEnabled
isVideoEnabled.value = callParams.isVideoEnabled && callParams.videoDirection == MediaDirection.SendRecv
Log.i("[Conference Waiting Room] Video will be ${if (callParams.isVideoEnabled) "enabled" else "disabled"} with direction ${callParams.videoDirection}")
isSwitchCameraAvailable.value = callParams.isVideoEnabled && coreContext.showSwitchCameraButton()
coreContext.core.isVideoPreviewEnabled = isVideoEnabled.value == true
}
private fun isVideoAvailableInCore(): Boolean {
val core = coreContext.core
return core.isVideoCaptureEnabled || core.isVideoPreviewEnabled
}
}

Some files were not shown because too many files have changed in this diff Show more