From 30ab11b06ad34a57d525d997dd5d87c6bfdcc604 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Adrien=20B=C3=A9raud?= <adrien.beraud@savoirfairelinux.com>
Date: Thu, 9 Sep 2021 23:54:32 -0400
Subject: [PATCH] more Kotlin migration, answer to profile received for account

Change-Id: I496518758bf7ee1bf93451ebf84772c4be08ed32
---
 .../account/AccountCreationModelImpl.java     |  62 --
 .../ring/account/AccountCreationModelImpl.kt  |  48 ++
 .../cx/ring/account/AccountEditionFragment.kt | 100 ++--
 .../cx/ring/account/AccountWizardActivity.kt  |  40 +-
 .../account/JamiAccountConnectFragment.java   |   3 +-
 .../account/JamiAccountCreationFragment.kt    |   4 +-
 .../account/JamiAccountPasswordFragment.java  |   2 +-
 .../account/JamiAccountSummaryFragment.kt     |  65 +--
 .../account/JamiAccountUsernameFragment.java  |   2 +-
 .../ring/account/JamiLinkAccountFragment.kt   |   4 +-
 .../JamiLinkAccountPasswordFragment.java      |   5 +-
 .../ring/account/ProfileCreationFragment.kt   |  13 +-
 .../cx/ring/account/RenameDeviceDialog.kt     |   2 +-
 .../cx/ring/adapters/ConversationAdapter.kt   |  77 +--
 .../cx/ring/adapters/RingtoneAdapter.java     | 175 ------
 .../java/cx/ring/adapters/RingtoneAdapter.kt  | 160 ++++++
 .../cx/ring/client/AccountSpinnerAdapter.kt   |  60 +-
 .../cx/ring/client/ContactDetailsActivity.kt  |  12 +-
 .../cx/ring/client/ConversationActivity.kt    |   2 +-
 .../client/ConversationSelectionActivity.kt   |   9 +-
 .../main/java/cx/ring/client/HomeActivity.kt  |  13 +-
 .../cx/ring/client/MediaViewerFragment.kt     |   6 +-
 .../java/cx/ring/client/RingtoneActivity.kt   |  69 +--
 .../main/java/cx/ring/client/ShareActivity.kt |   2 +
 .../ContactRequestsFragment.kt                |   6 +-
 .../fragments/AccountMigrationFragment.java   |   4 +-
 .../ring/fragments/AdvancedAccountFragment.kt |  33 +-
 .../fragments/AdvancedAccountPresenter.java   | 110 ----
 .../java/cx/ring/fragments/CallFragment.kt    |  30 +-
 .../cx/ring/fragments/CodecPreference.java    | 189 -------
 .../java/cx/ring/fragments/CodecPreference.kt | 142 +++++
 .../ring/fragments/ContactPickerFragment.java | 170 ------
 .../ring/fragments/ContactPickerFragment.kt   | 138 +++++
 .../cx/ring/fragments/ConversationFragment.kt |  33 +-
 .../fragments/GeneralAccountFragment.java     | 265 ---------
 .../ring/fragments/GeneralAccountFragment.kt  | 230 ++++++++
 .../fragments/GeneralAccountPresenter.java    | 141 -----
 .../cx/ring/fragments/LinkDeviceFragment.java |   1 -
 .../fragments/MediaPreferenceFragment.java    | 185 ------
 .../ring/fragments/MediaPreferenceFragment.kt | 159 ++++++
 .../fragments/MediaPreferencePresenter.java   |  99 ----
 .../fragments/PluginHandlersListFragment.kt   |  12 +-
 .../fragments/SIPAccountCreationFragment.kt   |  12 +-
 .../fragments/SecurityAccountFragment.java    | 317 -----------
 .../ring/fragments/SecurityAccountFragment.kt | 281 ++++++++++
 .../fragments/SecurityAccountPresenter.java   |  97 ----
 .../cx/ring/fragments/ShareWithFragment.kt    |   6 +-
 .../cx/ring/fragments/SmartListFragment.kt    |  24 +-
 ...Interface.java => BackHandlerInterface.kt} |   8 +-
 .../java/cx/ring/interfaces/Colorable.java    |   7 -
 .../main/java/cx/ring/interfaces/Colorable.kt |   7 +
 .../main/java/cx/ring/mvp/BaseActivity.java   |   4 +-
 .../cx/ring/mvp/BaseBottomSheetFragment.java  |  35 +-
 .../cx/ring/mvp/BasePreferenceFragment.java   |   5 +-
 .../java/cx/ring/mvp/BaseSupportFragment.kt   |  31 +-
 .../java/cx/ring/plugins/PluginUtils.java     | 187 -------
 .../main/java/cx/ring/plugins/PluginUtils.kt  | 140 +++++
 .../RecyclerPickerLayoutManager.java          | 100 ----
 .../RecyclerPickerLayoutManager.kt            |  92 +++
 .../java/cx/ring/service/BootReceiver.java    |  69 ---
 .../main/java/cx/ring/service/BootReceiver.kt |  57 ++
 .../ring/service/CallNotificationService.java |  71 ---
 .../ring/service/CallNotificationService.kt   |  64 +++
 .../cx/ring/service/OutgoingCallHandler.java  |  64 ---
 .../cx/ring/service/OutgoingCallHandler.kt    |  58 ++
 .../cx/ring/services/ContactServiceImpl.kt    | 105 ++--
 .../cx/ring/services/HardwareServiceImpl.kt   | 103 ++--
 ...{LogServiceImpl.java => LogServiceImpl.kt} |  44 +-
 .../ring/services/NotificationServiceImpl.kt  |  35 +-
 .../services/SharedPreferencesServiceImpl.kt  | 132 ++---
 .../java/cx/ring/services/VCardServiceImpl.kt |  27 +-
 .../cx/ring/settings/AccountFragment.java     | 137 -----
 .../java/cx/ring/settings/AccountFragment.kt  | 120 ++++
 .../pluginssettings/PathListAdapter.java      | 113 ----
 .../pluginssettings/PathListAdapter.kt        |  93 +++
 .../pluginssettings/PluginDetails.java        | 115 ----
 .../settings/pluginssettings/PluginDetails.kt |  71 +++
 .../PluginPathPreferenceFragment.java         | 210 -------
 .../PluginPathPreferenceFragment.kt           | 179 ++++++
 .../PluginPreferencesDataStore.java           | 186 ------
 .../PluginPreferencesDataStore.kt             | 118 ++++
 .../PluginSettingsFragment.java               | 382 -------------
 .../pluginssettings/PluginSettingsFragment.kt | 313 +++++++++++
 .../pluginssettings/PluginsListAdapter.java   |  99 ----
 .../pluginssettings/PluginsListAdapter.kt     |  75 +++
 .../PluginsListSettingsFragment.java          | 194 -------
 .../PluginsListSettingsFragment.kt            | 187 +++++++
 .../java/cx/ring/share/ShareFragment.java     | 103 ----
 .../main/java/cx/ring/share/ShareFragment.kt  |  84 +++
 .../ring/tv/about/AboutDetailsFragment.java   |   3 +-
 .../tv/account/JamiPreferenceFragment.java    |   3 +-
 .../cx/ring/tv/account/TVAccountExport.kt     |   9 +-
 .../cx/ring/tv/account/TVAccountWizard.kt     |  39 +-
 .../TVJamiAccountCreationFragment.java        |   5 +-
 .../tv/account/TVJamiLinkAccountFragment.java |   9 +-
 .../tv/account/TVProfileCreationFragment.kt   |  36 +-
 .../tv/account/TVProfileEditingFragment.kt    |  36 +-
 .../cx/ring/tv/account/TVShareFragment.kt     |  13 +-
 .../java/cx/ring/tv/call/TVCallActivity.kt    |   2 +-
 .../java/cx/ring/tv/call/TVCallFragment.kt    |  24 +-
 .../ring/tv/cards/contacts/ContactCard.java   |  65 ---
 .../cx/ring/tv/cards/contacts/ContactCard.kt  |  65 +++
 .../cards/contacts/ContactCardPresenter.java  |  66 ---
 .../tv/cards/contacts/ContactCardPresenter.kt |  49 ++
 .../cx/ring/tv/contact/TVContactFragment.kt   |  58 +-
 .../cx/ring/tv/contact/TVContactPresenter.kt  |   2 +-
 .../cx/ring/tv/contact/TVContactView.java     |   3 +-
 ...Activity.java => TVContactMoreActivity.kt} |  29 +-
 .../tv/contact/more/TVContactMoreFragment.kt  |  18 +-
 .../tv/conversation/TvConversationFragment.kt |  83 ++-
 .../cx/ring/tv/main/BaseBrowseFragment.java   |  20 +-
 .../cx/ring/tv/main/BaseDetailFragment.java   |  22 +-
 .../main/java/cx/ring/tv/main/HomeActivity.kt |   4 +-
 .../main/java/cx/ring/tv/main/MainFragment.kt | 101 ++--
 .../java/cx/ring/tv/main/MainPresenter.kt     |  18 +-
 .../main/java/cx/ring/tv/main/MainView.java   |   2 -
 .../java/cx/ring/tv/main/SpinnerFragment.java |  53 --
 .../java/cx/ring/tv/main/SpinnerFragment.kt   |  26 +
 .../cx/ring/tv/search/BaseSearchFragment.java |  31 +-
 .../ring/tv/search/ContactSearchFragment.kt   |   2 +-
 .../tv/search/ContactSearchPresenter.java     | 125 -----
 .../ring/tv/search/ContactSearchPresenter.kt  | 103 ++++
 .../cx/ring/tv/search/ContactSearchView.java  |   2 -
 .../ring/tv/settings/TVSettingsFragment.java  |   7 +-
 .../java/cx/ring/utils/AndroidFileUtils.kt    |  17 +-
 .../java/cx/ring/utils/ContentUriHandler.kt   |   1 -
 .../java/cx/ring/utils/ConversationPath.kt    |   8 +-
 .../main/java/cx/ring/utils/NetworkUtils.java |  59 --
 .../main/java/cx/ring/utils/NetworkUtils.kt   |  53 ++
 .../ring/viewholders/SmartListViewHolder.kt   |   4 +-
 .../main/java/cx/ring/views/AvatarDrawable.kt |  21 +-
 .../main/java/cx/ring/views/SwitchButton.java | 529 ------------------
 .../main/java/cx/ring/views/SwitchButton.kt   | 406 ++++++++++++++
 ring-android/build.gradle                     |   4 +-
 ring-android/libringclient/build.gradle       |   4 +-
 .../jami/account/AccountWizardPresenter.java  | 306 ----------
 .../jami/account/AccountWizardPresenter.kt    | 265 +++++++++
 .../net/jami/account/AccountWizardView.java   |  52 --
 ...ionPresenter.java => AccountWizardView.kt} |  46 +-
 .../account/HomeAccountCreationPresenter.kt}  |  29 +-
 .../HomeAccountCreationView.kt}               |  16 +-
 .../account/JamiAccountConnectPresenter.java  |  81 ---
 .../account/JamiAccountConnectPresenter.kt    |  72 +++
 .../account/JamiAccountCreationPresenter.kt   |  45 +-
 .../jami/account/JamiAccountCreationView.kt   |   6 +-
 .../account/JamiAccountSummaryPresenter.java  | 252 ---------
 .../account/JamiAccountSummaryPresenter.kt    | 218 ++++++++
 .../jami/account/JamiAccountSummaryView.java  |  73 ---
 .../jami/account/JamiAccountSummaryView.kt    |  48 ++
 .../jami/account/JamiConnectAccountView.kt}   |  16 +-
 .../account/JamiLinkAccountPresenter.java     |  75 ---
 .../jami/account/JamiLinkAccountPresenter.kt  |  65 +++
 ...ccountView.java => JamiLinkAccountView.kt} |  18 +-
 .../net/jami/account/LinkDevicePresenter.java |  88 ---
 .../net/jami/account/LinkDevicePresenter.kt   |  70 +++
 ...{LinkDeviceView.java => LinkDeviceView.kt} |  35 +-
 .../account/ProfileCreationPresenter.java     | 120 ----
 .../jami/account/ProfileCreationPresenter.kt  | 103 ++++
 .../net/jami/account/ProfileCreationView.java |  39 --
 .../net/jami/account/ProfileCreationView.kt}  |  25 +-
 .../net/jami/account/SIPCreationPresenter.kt  | 174 ++++++
 .../SIPCreationView.kt}                       |  32 +-
 .../jami/account/SecurityAccountPresenter.kt  |  82 +++
 .../net/jami/account/SecurityAccountView.kt   |  29 +
 .../main/java/net/jami/call/CallPresenter.kt  | 155 +++--
 .../src/main/java/net/jami/call/CallView.java |  91 ---
 .../src/main/java/net/jami/call/CallView.kt   |  61 ++
 .../contactrequests/BlockListPresenter.kt     |  28 +-
 .../ContactRequestsPresenter.kt               |   4 +-
 ...questsView.java => ContactRequestsView.kt} |  24 +-
 .../conversation/ConversationPresenter.kt     |  35 +-
 .../net/jami/conversation/ConversationView.kt |   5 +-
 .../src/main/java/net/jami/model/Account.kt   |  33 +-
 .../main/java/net/jami/model/AccountConfig.kt |   7 +-
 .../net/jami/model/AccountCreationModel.kt    |  76 +++
 .../net/jami/model/AccountCredentials.java    |  93 ---
 .../java/net/jami/model/AccountCredentials.kt |  62 ++
 .../src/main/java/net/jami/model/Codec.java   | 108 ----
 .../src/main/java/net/jami/model/Codec.kt     |  57 ++
 .../src/main/java/net/jami/model/Contact.kt   |  13 +-
 .../main/java/net/jami/model/Conversation.kt  |   6 +-
 .../net/jami/model/ConversationHistory.java   |  70 ---
 .../net/jami/model/ConversationHistory.kt     |  58 ++
 ...ransferError.java => DataTransferError.kt} |  10 +-
 .../{mvp/BaseView.java => model/Error.kt}     |  14 +-
 .../src/main/java/net/jami/model/Phone.java   | 128 -----
 .../src/main/java/net/jami/model/Phone.kt     |  86 +++
 .../src/main/java/net/jami/model/Profile.kt   |  23 +
 .../main/java/net/jami/model/Ringtone.java    |  79 ---
 .../src/main/java/net/jami/model/Ringtone.kt  |  25 +
 .../main/java/net/jami/model/Settings.java    | 124 ----
 .../src/main/java/net/jami/model/Settings.kt  |  58 ++
 .../src/main/java/net/jami/model/Uri.kt       |   4 -
 .../net/jami/mvp/AccountCreationModel.java    | 149 -----
 .../{RootPresenter.java => RootPresenter.kt}  |  50 +-
 .../navigation/HomeNavigationPresenter.java   | 193 -------
 .../navigation/HomeNavigationPresenter.kt     | 160 ++++++
 .../jami/navigation/HomeNavigationView.java   |  40 --
 .../HomeNavigationView.kt}                    |  22 +-
 .../navigation/HomeNavigationViewModel.kt     |   3 +-
 .../java/net/jami/services/AccountService.kt  |  58 +-
 .../java/net/jami/services/CallService.kt     |  22 +-
 .../java/net/jami/services/ContactService.kt  |   6 +-
 .../net/jami/services/ConversationFacade.kt   |  84 ++-
 .../java/net/jami/services/DaemonService.java | 423 --------------
 .../java/net/jami/services/DaemonService.kt   | 342 +++++++++++
 .../net/jami/services/DeviceRuntimeService.kt |   7 +-
 .../java/net/jami/services/HardwareService.kt |  47 +-
 .../java/net/jami/services/HistoryService.kt  |   8 +-
 .../jami/services/NotificationService.java    |  67 ---
 .../net/jami/services/NotificationService.kt  |  55 ++
 .../net/jami/services/PreferencesService.java | 100 ----
 .../net/jami/services/PreferencesService.kt   |  70 +++
 .../java/net/jami/services/VCardService.java  |  50 --
 .../java/net/jami/services/VCardService.kt    |  49 ++
 .../jami/settings/AdvancedAccountPresenter.kt |  97 ++++
 .../AdvancedAccountView.kt}                   |  15 +-
 .../jami/settings/GeneralAccountPresenter.kt  | 129 +++++
 .../net/jami/settings/GeneralAccountView.kt   |  31 +
 .../jami/settings/MediaPreferencePresenter.kt |  83 +++
 .../jami/settings}/MediaPreferenceView.java   |   2 +-
 .../net/jami/settings/SettingsPresenter.java  |  78 ---
 .../net/jami/settings/SettingsPresenter.kt    |  68 +++
 .../java/net/jami/share/SharePresenter.java   |  61 --
 .../java/net/jami/share/SharePresenter.kt     |  49 ++
 .../java/net/jami/share/ShareViewModel.java   |  54 --
 .../java/net/jami/share/ShareViewModel.kt     |  34 ++
 .../net/jami/smartlist/SmartListPresenter.kt  |  12 +-
 .../net/jami/smartlist/SmartListView.java     |  62 --
 .../java/net/jami/smartlist/SmartListView.kt  |  43 ++
 .../net/jami/smartlist/SmartListViewModel.kt  |   5 +-
 .../src/main/java/net/jami/utils/Log.java     |  63 ---
 .../src/main/java/net/jami/utils/Log.kt       |  65 +++
 .../jami/utils/NameLookupInputHandler.java    |  62 --
 .../net/jami/utils/NameLookupInputHandler.kt  |  46 ++
 .../main/java/net/jami/utils/QRCodeUtils.java |  99 ----
 .../main/java/net/jami/utils/QRCodeUtils.kt   |  66 +++
 .../main/java/net/jami/utils/VCardUtils.kt    |  22 +-
 .../net/jami/wizard/SIPCreationPresenter.java | 210 -------
 .../java/net/jami/model/ConversationTest.java | 182 ------
 .../java/net/jami/model/ConversationTest.kt   | 203 +++++++
 .../src/test/java/net/jami/model/UriTest.java |  70 ---
 .../src/test/java/net/jami/model/UriTest.kt   |  64 +++
 243 files changed, 7955 insertions(+), 10124 deletions(-)
 delete mode 100644 ring-android/app/src/main/java/cx/ring/account/AccountCreationModelImpl.java
 create mode 100644 ring-android/app/src/main/java/cx/ring/account/AccountCreationModelImpl.kt
 delete mode 100644 ring-android/app/src/main/java/cx/ring/adapters/RingtoneAdapter.java
 create mode 100644 ring-android/app/src/main/java/cx/ring/adapters/RingtoneAdapter.kt
 delete mode 100644 ring-android/app/src/main/java/cx/ring/fragments/AdvancedAccountPresenter.java
 delete mode 100644 ring-android/app/src/main/java/cx/ring/fragments/CodecPreference.java
 create mode 100644 ring-android/app/src/main/java/cx/ring/fragments/CodecPreference.kt
 delete mode 100644 ring-android/app/src/main/java/cx/ring/fragments/ContactPickerFragment.java
 create mode 100644 ring-android/app/src/main/java/cx/ring/fragments/ContactPickerFragment.kt
 delete mode 100644 ring-android/app/src/main/java/cx/ring/fragments/GeneralAccountFragment.java
 create mode 100644 ring-android/app/src/main/java/cx/ring/fragments/GeneralAccountFragment.kt
 delete mode 100644 ring-android/app/src/main/java/cx/ring/fragments/GeneralAccountPresenter.java
 delete mode 100644 ring-android/app/src/main/java/cx/ring/fragments/MediaPreferenceFragment.java
 create mode 100644 ring-android/app/src/main/java/cx/ring/fragments/MediaPreferenceFragment.kt
 delete mode 100644 ring-android/app/src/main/java/cx/ring/fragments/MediaPreferencePresenter.java
 delete mode 100644 ring-android/app/src/main/java/cx/ring/fragments/SecurityAccountFragment.java
 create mode 100644 ring-android/app/src/main/java/cx/ring/fragments/SecurityAccountFragment.kt
 delete mode 100644 ring-android/app/src/main/java/cx/ring/fragments/SecurityAccountPresenter.java
 rename ring-android/app/src/main/java/cx/ring/interfaces/{BackHandlerInterface.java => BackHandlerInterface.kt} (88%)
 delete mode 100644 ring-android/app/src/main/java/cx/ring/interfaces/Colorable.java
 create mode 100644 ring-android/app/src/main/java/cx/ring/interfaces/Colorable.kt
 delete mode 100644 ring-android/app/src/main/java/cx/ring/plugins/PluginUtils.java
 create mode 100644 ring-android/app/src/main/java/cx/ring/plugins/PluginUtils.kt
 delete mode 100644 ring-android/app/src/main/java/cx/ring/plugins/RecyclerPicker/RecyclerPickerLayoutManager.java
 create mode 100644 ring-android/app/src/main/java/cx/ring/plugins/RecyclerPicker/RecyclerPickerLayoutManager.kt
 delete mode 100644 ring-android/app/src/main/java/cx/ring/service/BootReceiver.java
 create mode 100644 ring-android/app/src/main/java/cx/ring/service/BootReceiver.kt
 delete mode 100644 ring-android/app/src/main/java/cx/ring/service/CallNotificationService.java
 create mode 100644 ring-android/app/src/main/java/cx/ring/service/CallNotificationService.kt
 delete mode 100644 ring-android/app/src/main/java/cx/ring/service/OutgoingCallHandler.java
 create mode 100644 ring-android/app/src/main/java/cx/ring/service/OutgoingCallHandler.kt
 rename ring-android/app/src/main/java/cx/ring/services/{LogServiceImpl.java => LogServiceImpl.kt} (52%)
 delete mode 100644 ring-android/app/src/main/java/cx/ring/settings/AccountFragment.java
 create mode 100644 ring-android/app/src/main/java/cx/ring/settings/AccountFragment.kt
 delete mode 100644 ring-android/app/src/main/java/cx/ring/settings/pluginssettings/PathListAdapter.java
 create mode 100644 ring-android/app/src/main/java/cx/ring/settings/pluginssettings/PathListAdapter.kt
 delete mode 100644 ring-android/app/src/main/java/cx/ring/settings/pluginssettings/PluginDetails.java
 create mode 100644 ring-android/app/src/main/java/cx/ring/settings/pluginssettings/PluginDetails.kt
 delete mode 100644 ring-android/app/src/main/java/cx/ring/settings/pluginssettings/PluginPathPreferenceFragment.java
 create mode 100644 ring-android/app/src/main/java/cx/ring/settings/pluginssettings/PluginPathPreferenceFragment.kt
 delete mode 100644 ring-android/app/src/main/java/cx/ring/settings/pluginssettings/PluginPreferencesDataStore.java
 create mode 100644 ring-android/app/src/main/java/cx/ring/settings/pluginssettings/PluginPreferencesDataStore.kt
 delete mode 100644 ring-android/app/src/main/java/cx/ring/settings/pluginssettings/PluginSettingsFragment.java
 create mode 100644 ring-android/app/src/main/java/cx/ring/settings/pluginssettings/PluginSettingsFragment.kt
 delete mode 100644 ring-android/app/src/main/java/cx/ring/settings/pluginssettings/PluginsListAdapter.java
 create mode 100644 ring-android/app/src/main/java/cx/ring/settings/pluginssettings/PluginsListAdapter.kt
 delete mode 100644 ring-android/app/src/main/java/cx/ring/settings/pluginssettings/PluginsListSettingsFragment.java
 create mode 100644 ring-android/app/src/main/java/cx/ring/settings/pluginssettings/PluginsListSettingsFragment.kt
 delete mode 100644 ring-android/app/src/main/java/cx/ring/share/ShareFragment.java
 create mode 100644 ring-android/app/src/main/java/cx/ring/share/ShareFragment.kt
 delete mode 100644 ring-android/app/src/main/java/cx/ring/tv/cards/contacts/ContactCard.java
 create mode 100644 ring-android/app/src/main/java/cx/ring/tv/cards/contacts/ContactCard.kt
 delete mode 100644 ring-android/app/src/main/java/cx/ring/tv/cards/contacts/ContactCardPresenter.java
 create mode 100644 ring-android/app/src/main/java/cx/ring/tv/cards/contacts/ContactCardPresenter.kt
 rename ring-android/app/src/main/java/cx/ring/tv/contact/more/{TVContactMoreActivity.java => TVContactMoreActivity.kt} (64%)
 delete mode 100644 ring-android/app/src/main/java/cx/ring/tv/main/SpinnerFragment.java
 create mode 100644 ring-android/app/src/main/java/cx/ring/tv/main/SpinnerFragment.kt
 delete mode 100644 ring-android/app/src/main/java/cx/ring/tv/search/ContactSearchPresenter.java
 create mode 100644 ring-android/app/src/main/java/cx/ring/tv/search/ContactSearchPresenter.kt
 delete mode 100644 ring-android/app/src/main/java/cx/ring/utils/NetworkUtils.java
 create mode 100644 ring-android/app/src/main/java/cx/ring/utils/NetworkUtils.kt
 delete mode 100644 ring-android/app/src/main/java/cx/ring/views/SwitchButton.java
 create mode 100644 ring-android/app/src/main/java/cx/ring/views/SwitchButton.kt
 delete mode 100644 ring-android/libringclient/src/main/java/net/jami/account/AccountWizardPresenter.java
 create mode 100644 ring-android/libringclient/src/main/java/net/jami/account/AccountWizardPresenter.kt
 delete mode 100644 ring-android/libringclient/src/main/java/net/jami/account/AccountWizardView.java
 rename ring-android/libringclient/src/main/java/net/jami/account/{HomeAccountCreationPresenter.java => AccountWizardView.kt} (55%)
 rename ring-android/{app/src/main/java/cx/ring/fragments/GeneralAccountView.java => libringclient/src/main/java/net/jami/account/HomeAccountCreationPresenter.kt} (67%)
 rename ring-android/libringclient/src/main/java/net/jami/{model/Error.java => account/HomeAccountCreationView.kt} (83%)
 delete mode 100644 ring-android/libringclient/src/main/java/net/jami/account/JamiAccountConnectPresenter.java
 create mode 100644 ring-android/libringclient/src/main/java/net/jami/account/JamiAccountConnectPresenter.kt
 delete mode 100644 ring-android/libringclient/src/main/java/net/jami/account/JamiAccountSummaryPresenter.java
 create mode 100644 ring-android/libringclient/src/main/java/net/jami/account/JamiAccountSummaryPresenter.kt
 delete mode 100644 ring-android/libringclient/src/main/java/net/jami/account/JamiAccountSummaryView.java
 create mode 100644 ring-android/libringclient/src/main/java/net/jami/account/JamiAccountSummaryView.kt
 rename ring-android/{app/src/main/java/cx/ring/fragments/AdvancedAccountView.java => libringclient/src/main/java/net/jami/account/JamiConnectAccountView.kt} (78%)
 delete mode 100644 ring-android/libringclient/src/main/java/net/jami/account/JamiLinkAccountPresenter.java
 create mode 100644 ring-android/libringclient/src/main/java/net/jami/account/JamiLinkAccountPresenter.kt
 rename ring-android/libringclient/src/main/java/net/jami/account/{JamiConnectAccountView.java => JamiLinkAccountView.kt} (76%)
 delete mode 100644 ring-android/libringclient/src/main/java/net/jami/account/LinkDevicePresenter.java
 create mode 100644 ring-android/libringclient/src/main/java/net/jami/account/LinkDevicePresenter.kt
 rename ring-android/libringclient/src/main/java/net/jami/account/{LinkDeviceView.java => LinkDeviceView.kt} (70%)
 delete mode 100644 ring-android/libringclient/src/main/java/net/jami/account/ProfileCreationPresenter.java
 create mode 100644 ring-android/libringclient/src/main/java/net/jami/account/ProfileCreationPresenter.kt
 delete mode 100644 ring-android/libringclient/src/main/java/net/jami/account/ProfileCreationView.java
 rename ring-android/{app/src/main/java/cx/ring/fragments/SecurityAccountView.java => libringclient/src/main/java/net/jami/account/ProfileCreationView.kt} (68%)
 create mode 100644 ring-android/libringclient/src/main/java/net/jami/account/SIPCreationPresenter.kt
 rename ring-android/libringclient/src/main/java/net/jami/{mvp/SIPCreationView.java => account/SIPCreationView.kt} (72%)
 create mode 100644 ring-android/libringclient/src/main/java/net/jami/account/SecurityAccountPresenter.kt
 create mode 100644 ring-android/libringclient/src/main/java/net/jami/account/SecurityAccountView.kt
 delete mode 100644 ring-android/libringclient/src/main/java/net/jami/call/CallView.java
 create mode 100644 ring-android/libringclient/src/main/java/net/jami/call/CallView.kt
 rename ring-android/libringclient/src/main/java/net/jami/contactrequests/{ContactRequestsView.java => ContactRequestsView.kt} (64%)
 create mode 100644 ring-android/libringclient/src/main/java/net/jami/model/AccountCreationModel.kt
 delete mode 100644 ring-android/libringclient/src/main/java/net/jami/model/AccountCredentials.java
 create mode 100644 ring-android/libringclient/src/main/java/net/jami/model/AccountCredentials.kt
 delete mode 100644 ring-android/libringclient/src/main/java/net/jami/model/Codec.java
 create mode 100644 ring-android/libringclient/src/main/java/net/jami/model/Codec.kt
 delete mode 100644 ring-android/libringclient/src/main/java/net/jami/model/ConversationHistory.java
 create mode 100644 ring-android/libringclient/src/main/java/net/jami/model/ConversationHistory.kt
 rename ring-android/libringclient/src/main/java/net/jami/model/{DataTransferError.java => DataTransferError.kt} (88%)
 rename ring-android/libringclient/src/main/java/net/jami/{mvp/BaseView.java => model/Error.kt} (85%)
 delete mode 100644 ring-android/libringclient/src/main/java/net/jami/model/Phone.java
 create mode 100644 ring-android/libringclient/src/main/java/net/jami/model/Phone.kt
 create mode 100644 ring-android/libringclient/src/main/java/net/jami/model/Profile.kt
 delete mode 100644 ring-android/libringclient/src/main/java/net/jami/model/Ringtone.java
 create mode 100644 ring-android/libringclient/src/main/java/net/jami/model/Ringtone.kt
 delete mode 100644 ring-android/libringclient/src/main/java/net/jami/model/Settings.java
 create mode 100644 ring-android/libringclient/src/main/java/net/jami/model/Settings.kt
 delete mode 100644 ring-android/libringclient/src/main/java/net/jami/mvp/AccountCreationModel.java
 rename ring-android/libringclient/src/main/java/net/jami/mvp/{RootPresenter.java => RootPresenter.kt} (54%)
 delete mode 100644 ring-android/libringclient/src/main/java/net/jami/navigation/HomeNavigationPresenter.java
 create mode 100644 ring-android/libringclient/src/main/java/net/jami/navigation/HomeNavigationPresenter.kt
 delete mode 100644 ring-android/libringclient/src/main/java/net/jami/navigation/HomeNavigationView.java
 rename ring-android/libringclient/src/main/java/net/jami/{account/JamiLinkAccountView.java => navigation/HomeNavigationView.kt} (74%)
 delete mode 100644 ring-android/libringclient/src/main/java/net/jami/services/DaemonService.java
 create mode 100644 ring-android/libringclient/src/main/java/net/jami/services/DaemonService.kt
 delete mode 100644 ring-android/libringclient/src/main/java/net/jami/services/NotificationService.java
 create mode 100644 ring-android/libringclient/src/main/java/net/jami/services/NotificationService.kt
 delete mode 100644 ring-android/libringclient/src/main/java/net/jami/services/PreferencesService.java
 create mode 100644 ring-android/libringclient/src/main/java/net/jami/services/PreferencesService.kt
 delete mode 100644 ring-android/libringclient/src/main/java/net/jami/services/VCardService.java
 create mode 100644 ring-android/libringclient/src/main/java/net/jami/services/VCardService.kt
 create mode 100644 ring-android/libringclient/src/main/java/net/jami/settings/AdvancedAccountPresenter.kt
 rename ring-android/libringclient/src/main/java/net/jami/{account/HomeAccountCreationView.java => settings/AdvancedAccountView.kt} (80%)
 create mode 100644 ring-android/libringclient/src/main/java/net/jami/settings/GeneralAccountPresenter.kt
 create mode 100644 ring-android/libringclient/src/main/java/net/jami/settings/GeneralAccountView.kt
 create mode 100644 ring-android/libringclient/src/main/java/net/jami/settings/MediaPreferencePresenter.kt
 rename ring-android/{app/src/main/java/cx/ring/fragments => libringclient/src/main/java/net/jami/settings}/MediaPreferenceView.java (97%)
 delete mode 100644 ring-android/libringclient/src/main/java/net/jami/settings/SettingsPresenter.java
 create mode 100644 ring-android/libringclient/src/main/java/net/jami/settings/SettingsPresenter.kt
 delete mode 100644 ring-android/libringclient/src/main/java/net/jami/share/SharePresenter.java
 create mode 100644 ring-android/libringclient/src/main/java/net/jami/share/SharePresenter.kt
 delete mode 100644 ring-android/libringclient/src/main/java/net/jami/share/ShareViewModel.java
 create mode 100644 ring-android/libringclient/src/main/java/net/jami/share/ShareViewModel.kt
 delete mode 100644 ring-android/libringclient/src/main/java/net/jami/smartlist/SmartListView.java
 create mode 100644 ring-android/libringclient/src/main/java/net/jami/smartlist/SmartListView.kt
 delete mode 100644 ring-android/libringclient/src/main/java/net/jami/utils/Log.java
 create mode 100644 ring-android/libringclient/src/main/java/net/jami/utils/Log.kt
 delete mode 100644 ring-android/libringclient/src/main/java/net/jami/utils/NameLookupInputHandler.java
 create mode 100644 ring-android/libringclient/src/main/java/net/jami/utils/NameLookupInputHandler.kt
 delete mode 100644 ring-android/libringclient/src/main/java/net/jami/utils/QRCodeUtils.java
 create mode 100644 ring-android/libringclient/src/main/java/net/jami/utils/QRCodeUtils.kt
 delete mode 100644 ring-android/libringclient/src/main/java/net/jami/wizard/SIPCreationPresenter.java
 delete mode 100644 ring-android/libringclient/src/test/java/net/jami/model/ConversationTest.java
 create mode 100644 ring-android/libringclient/src/test/java/net/jami/model/ConversationTest.kt
 delete mode 100644 ring-android/libringclient/src/test/java/net/jami/model/UriTest.java
 create mode 100644 ring-android/libringclient/src/test/java/net/jami/model/UriTest.kt

diff --git a/ring-android/app/src/main/java/cx/ring/account/AccountCreationModelImpl.java b/ring-android/app/src/main/java/cx/ring/account/AccountCreationModelImpl.java
deleted file mode 100644
index aa4173dd1..000000000
--- a/ring-android/app/src/main/java/cx/ring/account/AccountCreationModelImpl.java
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- *  Copyright (C) 2004-2021 Savoir-faire Linux Inc.
- *
- *  Author: Hadrien De Sousa <hadrien.desousa@savoirfairelinux.com>
- *
- *  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, write to the Free Software
- *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-package cx.ring.account;
-
-import android.graphics.Bitmap;
-
-import java.io.Serializable;
-
-import net.jami.mvp.AccountCreationModel;
-import cx.ring.utils.BitmapUtils;
-import ezvcard.VCard;
-import ezvcard.property.FormattedName;
-import ezvcard.property.Photo;
-import ezvcard.property.RawProperty;
-import ezvcard.property.Uid;
-import io.reactivex.rxjava3.core.Single;
-
-public class AccountCreationModelImpl extends AccountCreationModel implements Serializable {
-
-    @Override
-    public Single<VCard> toVCard() {
-        return Single
-                .fromCallable(() -> {
-                    VCard vcard = new VCard();
-                    vcard.setFormattedName(new FormattedName(getFullName()));
-                    vcard.setUid(new Uid(getUsername()));
-                    Bitmap bmp = getPhoto();
-                    if (bmp != null) {
-                        vcard.removeProperties(Photo.class);
-                        vcard.addPhoto(BitmapUtils.bitmapToPhoto(bmp));
-                    }
-                    vcard.removeProperties(RawProperty.class);
-                    return vcard;
-                });
-    }
-
-    @Override
-    public Bitmap getPhoto() {
-        return (Bitmap) super.getPhoto();
-    }
-
-    public void setPhoto(Bitmap photo) {
-        super.setPhoto(photo);
-    }
-}
diff --git a/ring-android/app/src/main/java/cx/ring/account/AccountCreationModelImpl.kt b/ring-android/app/src/main/java/cx/ring/account/AccountCreationModelImpl.kt
new file mode 100644
index 000000000..e548adfa1
--- /dev/null
+++ b/ring-android/app/src/main/java/cx/ring/account/AccountCreationModelImpl.kt
@@ -0,0 +1,48 @@
+/*
+ *  Copyright (C) 2004-2021 Savoir-faire Linux Inc.
+ *
+ *  Author: Hadrien De Sousa <hadrien.desousa@savoirfairelinux.com>
+ *
+ *  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, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+package cx.ring.account
+
+import net.jami.model.AccountCreationModel
+import ezvcard.VCard
+import ezvcard.property.FormattedName
+import ezvcard.property.Uid
+import android.graphics.Bitmap
+import cx.ring.utils.BitmapUtils
+import ezvcard.property.Photo
+import ezvcard.property.RawProperty
+import io.reactivex.rxjava3.core.Single
+import java.io.Serializable
+
+class AccountCreationModelImpl : AccountCreationModel(), Serializable {
+    override fun toVCard(): Single<VCard> {
+        return Single.fromCallable {
+            val vcard = VCard()
+            vcard.formattedName = FormattedName(fullName)
+            vcard.uid = Uid(username)
+            val bmp = photo as Bitmap?
+            if (bmp != null) {
+                vcard.removeProperties(Photo::class.java)
+                vcard.addPhoto(BitmapUtils.bitmapToPhoto(bmp))
+            }
+            vcard.removeProperties(RawProperty::class.java)
+            vcard
+        }
+    }
+}
\ No newline at end of file
diff --git a/ring-android/app/src/main/java/cx/ring/account/AccountEditionFragment.kt b/ring-android/app/src/main/java/cx/ring/account/AccountEditionFragment.kt
index 19f84f4ea..d69e930e7 100644
--- a/ring-android/app/src/main/java/cx/ring/account/AccountEditionFragment.kt
+++ b/ring-android/app/src/main/java/cx/ring/account/AccountEditionFragment.kt
@@ -53,13 +53,8 @@ class AccountEditionFragment : BaseSupportFragment<AccountEditionPresenter, Acco
     private var mAccountId: String? = null
     private var mAccountIsJami = false
 
-    override fun onCreateView(
-        inflater: LayoutInflater,
-        container: ViewGroup?,
-        savedInstanceState: Bundle?
-    ): View {
-        mBinding = FragAccountSettingsBinding.inflate(inflater, container, false)
-        return mBinding!!.root
+    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
+        return FragAccountSettingsBinding.inflate(inflater, container, false).apply { mBinding = this }.root
     }
 
     override fun onDestroyView() {
@@ -102,33 +97,32 @@ class AccountEditionFragment : BaseSupportFragment<AccountEditionPresenter, Acco
     }
 
     override fun initViewPager(accountId: String, isJami: Boolean) {
-        mBinding!!.pager.offscreenPageLimit = 4
-        mBinding!!.slidingTabs.setupWithViewPager(mBinding!!.pager)
-        mBinding!!.pager.adapter =
-            PreferencesPagerAdapter(childFragmentManager, activity, accountId, isJami)
-        val existingFragment =
-            childFragmentManager.findFragmentByTag(BlockListFragment.TAG) as BlockListFragment?
+        mBinding?.apply {
+            pager.offscreenPageLimit = 4
+            pager.adapter = PreferencesPagerAdapter(childFragmentManager, requireContext(), accountId, isJami)
+            slidingTabs.setupWithViewPager(pager)
+        }
+        val existingFragment = childFragmentManager.findFragmentByTag(BlockListFragment.TAG) as BlockListFragment?
         if (existingFragment != null) {
-            val args = Bundle()
-            args.putString(ACCOUNT_ID_KEY, accountId)
-            if (!existingFragment.isStateSaved) existingFragment.arguments = args
+            if (!existingFragment.isStateSaved)
+                existingFragment.arguments = Bundle().apply { putString(ACCOUNT_ID_KEY, accountId) }
             existingFragment.setAccount(accountId)
         }
     }
 
     override fun goToBlackList(accountId: String) {
         val blockListFragment = BlockListFragment()
-        val args = Bundle()
-        args.putString(ACCOUNT_ID_KEY, accountId)
-        blockListFragment.arguments = args
+        blockListFragment.arguments = Bundle().apply { putString(ACCOUNT_ID_KEY, accountId) }
         childFragmentManager.beginTransaction()
             .setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE)
             .addToBackStack(BlockListFragment.TAG)
             .replace(R.id.fragment_container, blockListFragment, BlockListFragment.TAG)
             .commit()
-        mBinding!!.slidingTabs.visibility = View.GONE
-        mBinding!!.pager.visibility = View.GONE
-        mBinding!!.fragmentContainer.visibility = View.VISIBLE
+        mBinding?.apply {
+            slidingTabs.visibility = View.GONE
+            pager.visibility = View.GONE
+            fragmentContainer.visibility = View.VISIBLE
+        }
     }
 
     override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
@@ -148,45 +142,45 @@ class AccountEditionFragment : BaseSupportFragment<AccountEditionPresenter, Acco
     override fun onBackPressed(): Boolean {
         if (mBinding == null) return false
         mIsVisible = false
-        if (activity is HomeActivity) (activity as HomeActivity?)!!.setToolbarOutlineState(true)
+        if (activity is HomeActivity) (activity as HomeActivity).setToolbarOutlineState(true)
         if (mBinding!!.fragmentContainer.visibility != View.VISIBLE) {
             toggleView(mAccountId, mAccountIsJami)
             return true
         }
-        val summaryFragment =
-            childFragmentManager.findFragmentByTag(JamiAccountSummaryFragment.TAG) as JamiAccountSummaryFragment?
-        return if (summaryFragment != null && summaryFragment.onBackPressed()) {
+        val summaryFragment = childFragmentManager.findFragmentByTag(JamiAccountSummaryFragment.TAG) as JamiAccountSummaryFragment?
+        return if (summaryFragment != null && summaryFragment.onBackPressed())
             true
-        } else childFragmentManager.popBackStackImmediate()
+        else
+            childFragmentManager.popBackStackImmediate()
     }
 
     private fun toggleView(accountId: String?, isJami: Boolean) {
         mAccountId = accountId
         mAccountIsJami = isJami
-        mBinding!!.slidingTabs.visibility = if (isJami) View.GONE else View.VISIBLE
-        mBinding!!.pager.visibility = if (isJami) View.GONE else View.VISIBLE
-        mBinding!!.fragmentContainer.visibility = if (isJami) View.VISIBLE else View.GONE
+        mBinding?.apply {
+            slidingTabs.visibility = if (isJami) View.GONE else View.VISIBLE
+            pager.visibility = if (isJami) View.GONE else View.VISIBLE
+            fragmentContainer.visibility = if (isJami) View.VISIBLE else View.GONE
+        }
         setBackListenerEnabled(isJami)
     }
 
     override fun exit() {
-        val activity: Activity? = activity
         activity?.onBackPressed()
     }
 
     private fun setBackListenerEnabled(enable: Boolean) {
         val activity: Activity? = activity
-        if (activity is HomeActivity) activity.setAccountFragmentOnBackPressedListener(if (enable) this else null)
+        if (activity is HomeActivity)
+            activity.setAccountFragmentOnBackPressedListener(if (enable) this else null)
     }
 
-    private class PreferencesPagerAdapter internal constructor(
-        fm: FragmentManager?,
-        private val mContext: Context?,
+    private class PreferencesPagerAdapter constructor(
+        fm: FragmentManager,
+        private val mContext: Context,
         private val accountId: String,
         private val isJamiAccount: Boolean
-    ) : FragmentStatePagerAdapter(
-        fm!!
-    ) {
+    ) : FragmentStatePagerAdapter(fm) {
         override fun getCount(): Int {
             return if (isJamiAccount) 3 else 4
         }
@@ -195,10 +189,9 @@ class AccountEditionFragment : BaseSupportFragment<AccountEditionPresenter, Acco
             return if (isJamiAccount) getJamiPanel(position) else getSIPPanel(position)
         }
 
-        override fun getPageTitle(position: Int): CharSequence? {
-            val resId =
-                if (isJamiAccount) getRingPanelTitle(position) else getSIPPanelTitle(position)
-            return mContext!!.getString(resId)
+        override fun getPageTitle(position: Int): CharSequence {
+            val resId = if (isJamiAccount) getRingPanelTitle(position) else getSIPPanelTitle(position)
+            return mContext.getString(resId)
         }
 
         private fun getJamiPanel(position: Int): Fragment {
@@ -221,10 +214,9 @@ class AccountEditionFragment : BaseSupportFragment<AccountEditionPresenter, Acco
         }
 
         private fun fragmentWithBundle(result: Fragment): Fragment {
-            val args = Bundle()
-            args.putString(ACCOUNT_ID_KEY, accountId)
-            result.arguments = args
-            return result
+            return result.apply {
+                arguments = Bundle().apply { putString(ACCOUNT_ID_KEY, accountId) }
+            }
         }
 
         companion object {
@@ -256,20 +248,19 @@ class AccountEditionFragment : BaseSupportFragment<AccountEditionPresenter, Acco
     }
 
     private fun setupElevation() {
-        if (mBinding == null || !mIsVisible) {
+        val binding = mBinding ?: return
+        if (!mIsVisible)
             return
-        }
         val activity: FragmentActivity = activity as? HomeActivity ?: return
-        val ll = mBinding!!.pager.getChildAt(mBinding!!.pager.currentItem) as LinearLayout
+        val ll = binding.pager.getChildAt(binding.pager.currentItem) as LinearLayout
         val rv = (ll.getChildAt(0) as FrameLayout).getChildAt(0) as RecyclerView
         val homeActivity = activity as HomeActivity
         if (rv.canScrollVertically(SCROLL_DIRECTION_UP)) {
-            mBinding!!.slidingTabs.elevation =
-                mBinding!!.slidingTabs.resources.getDimension(R.dimen.toolbar_elevation)
+            binding.slidingTabs.elevation = binding.slidingTabs.resources.getDimension(R.dimen.toolbar_elevation)
             homeActivity.setToolbarElevation(true)
             homeActivity.setToolbarOutlineState(false)
         } else {
-            mBinding!!.slidingTabs.elevation = 0f
+            binding.slidingTabs.elevation = 0f
             homeActivity.setToolbarElevation(false)
             homeActivity.setToolbarOutlineState(true)
         }
@@ -278,10 +269,9 @@ class AccountEditionFragment : BaseSupportFragment<AccountEditionPresenter, Acco
     companion object {
         private val TAG = AccountEditionFragment::class.simpleName
         @JvmField
-        val ACCOUNT_ID_KEY = AccountEditionFragment::class.qualifiedName + "accountid"
+        val ACCOUNT_ID_KEY = AccountEditionFragment::class.qualifiedName + "accountId"
         @JvmField
-        val ACCOUNT_HAS_PASSWORD_KEY =
-            AccountEditionFragment::class.qualifiedName + "hasPassword"
+        val ACCOUNT_HAS_PASSWORD_KEY = AccountEditionFragment::class.qualifiedName + "hasPassword"
         private const val SCROLL_DIRECTION_UP = -1
     }
 }
\ No newline at end of file
diff --git a/ring-android/app/src/main/java/cx/ring/account/AccountWizardActivity.kt b/ring-android/app/src/main/java/cx/ring/account/AccountWizardActivity.kt
index 4beaf67e2..cefb308a1 100644
--- a/ring-android/app/src/main/java/cx/ring/account/AccountWizardActivity.kt
+++ b/ring-android/app/src/main/java/cx/ring/account/AccountWizardActivity.kt
@@ -21,7 +21,6 @@
 package cx.ring.account
 
 import android.app.ProgressDialog
-import android.content.DialogInterface
 import android.content.Intent
 import android.content.pm.ActivityInfo
 import android.os.Bundle
@@ -36,6 +35,7 @@ import cx.ring.client.HomeActivity
 import cx.ring.fragments.AccountMigrationFragment
 import cx.ring.fragments.SIPAccountCreationFragment
 import cx.ring.mvp.BaseActivity
+import cx.ring.services.VCardServiceImpl
 import dagger.hilt.android.AndroidEntryPoint
 import ezvcard.VCard
 import io.reactivex.rxjava3.core.Single
@@ -44,7 +44,7 @@ import net.jami.account.AccountWizardPresenter
 import net.jami.account.AccountWizardView
 import net.jami.model.Account
 import net.jami.model.AccountConfig
-import net.jami.mvp.AccountCreationModel
+import net.jami.model.AccountCreationModel
 import net.jami.utils.VCardUtils
 
 @AndroidEntryPoint
@@ -71,29 +71,27 @@ class AccountWizardActivity : BaseActivity<AccountWizardPresenter>(), AccountWiz
         }
         if (savedInstanceState == null) {
             if (accountToMigrate != null) {
-                val args = Bundle()
-                args.putString(AccountMigrationFragment.ACCOUNT_ID, getIntent().data!!.lastPathSegment)
-                val fragment: Fragment = AccountMigrationFragment()
-                fragment.arguments = args
-                val fragmentManager = supportFragmentManager
-                fragmentManager
+                val fragment = AccountMigrationFragment().apply {
+                    arguments = Bundle().apply { putString(AccountEditionFragment.ACCOUNT_ID_KEY, accountToMigrate) }
+                }
+                supportFragmentManager
                     .beginTransaction()
                     .replace(R.id.wizard_container, fragment)
                     .commit()
             } else {
-                presenter.init(if (getIntent().action != null) getIntent().action else AccountConfig.ACCOUNT_TYPE_RING)
+                presenter.init(getIntent().action ?: AccountConfig.ACCOUNT_TYPE_RING)
             }
         }
     }
 
     override fun onDestroy() {
-        if (mProgress != null) {
-            mProgress!!.dismiss()
+        mProgress?.let { progress ->
+            progress.dismiss()
             mProgress = null
         }
-        if (mAlertDialog != null) {
-            mAlertDialog!!.setOnDismissListener(null)
-            mAlertDialog!!.dismiss()
+        mAlertDialog?.let { alertDialog ->
+            alertDialog.setOnDismissListener(null)
+            alertDialog.dismiss()
             mAlertDialog = null
         }
         super.onDestroy()
@@ -103,7 +101,7 @@ class AccountWizardActivity : BaseActivity<AccountWizardPresenter>(), AccountWiz
         val filedir = filesDir
         return accountCreationModel.toVCard()
             .flatMap { vcard: VCard ->
-                account.resetProfile()
+                account.loadedProfile = Single.fromCallable { VCardServiceImpl.readData(vcard) }.cache()
                 VCardUtils.saveLocalProfileToDisk(vcard, account.accountID, filedir)
             }
             .subscribeOn(Schedulers.io())
@@ -134,14 +132,14 @@ class AccountWizardActivity : BaseActivity<AccountWizardPresenter>(), AccountWiz
             .commit()
     }
 
-    override fun goToProfileCreation(model: AccountCreationModel) {
+    override fun goToProfileCreation(accountCreationModel: AccountCreationModel) {
         val fragments = supportFragmentManager.fragments
         if (fragments.size > 0) {
             val fragment = fragments[0]
             if (fragment is JamiLinkAccountFragment) {
-                fragment.scrollPagerFragment(model)
+                fragment.scrollPagerFragment(accountCreationModel)
             } else if (fragment is JamiAccountConnectFragment) {
-                profileCreated(model, false)
+                profileCreated(accountCreationModel, false)
             }
         }
     }
@@ -217,7 +215,7 @@ class AccountWizardActivity : BaseActivity<AccountWizardPresenter>(), AccountWiz
             .setPositiveButton(android.R.string.ok, null)
             .setTitle(R.string.account_cannot_be_found_title)
             .setMessage(R.string.account_cannot_be_found_message)
-            .setOnDismissListener { dialogInterface: DialogInterface? -> supportFragmentManager.popBackStack() }
+            .setOnDismissListener { supportFragmentManager.popBackStack() }
             .show()
     }
 
@@ -231,11 +229,11 @@ class AccountWizardActivity : BaseActivity<AccountWizardPresenter>(), AccountWiz
         presenter.successDialogClosed()
     }
 
-    fun profileCreated(accountCreationModel: AccountCreationModel?, saveProfile: Boolean) {
+    fun profileCreated(accountCreationModel: AccountCreationModel, saveProfile: Boolean) {
         presenter.profileCreated(accountCreationModel, saveProfile)
     }
 
     companion object {
-        val TAG = AccountWizardActivity::class.java.name
+        val TAG = AccountWizardActivity::class.simpleName!!
     }
 }
\ No newline at end of file
diff --git a/ring-android/app/src/main/java/cx/ring/account/JamiAccountConnectFragment.java b/ring-android/app/src/main/java/cx/ring/account/JamiAccountConnectFragment.java
index ded0fab55..04b909ed3 100644
--- a/ring-android/app/src/main/java/cx/ring/account/JamiAccountConnectFragment.java
+++ b/ring-android/app/src/main/java/cx/ring/account/JamiAccountConnectFragment.java
@@ -31,12 +31,11 @@ import android.view.inputmethod.EditorInfo;
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 
-import cx.ring.application.JamiApplication;
 import cx.ring.databinding.FragAccJamiConnectBinding;
 
 import net.jami.account.JamiAccountConnectPresenter;
 import net.jami.account.JamiConnectAccountView;
-import net.jami.mvp.AccountCreationModel;
+import net.jami.model.AccountCreationModel;
 import cx.ring.mvp.BaseSupportFragment;
 import dagger.hilt.android.AndroidEntryPoint;
 
diff --git a/ring-android/app/src/main/java/cx/ring/account/JamiAccountCreationFragment.kt b/ring-android/app/src/main/java/cx/ring/account/JamiAccountCreationFragment.kt
index 224be8c8c..349247325 100644
--- a/ring-android/app/src/main/java/cx/ring/account/JamiAccountCreationFragment.kt
+++ b/ring-android/app/src/main/java/cx/ring/account/JamiAccountCreationFragment.kt
@@ -33,7 +33,7 @@ import androidx.fragment.app.FragmentStatePagerAdapter
 import androidx.viewpager.widget.ViewPager.OnPageChangeListener
 import cx.ring.databinding.FragAccJamiCreateBinding
 import cx.ring.views.WizardViewPager
-import net.jami.mvp.AccountCreationModel
+import net.jami.model.AccountCreationModel
 
 class JamiAccountCreationFragment : Fragment() {
     private var mBinding: FragAccJamiCreateBinding? = null
@@ -42,7 +42,7 @@ class JamiAccountCreationFragment : Fragment() {
             override fun handleOnBackPressed() {
                 if (mCurrentFragment is ProfileCreationFragment) {
                     val fragment = mCurrentFragment as ProfileCreationFragment
-                    (activity as AccountWizardActivity?)?.profileCreated(fragment.model, false)
+                    (activity as AccountWizardActivity?)?.profileCreated(fragment.model!!, false)
                     return
                 }
                 mBinding!!.pager.currentItem = mBinding!!.pager.currentItem - 1
diff --git a/ring-android/app/src/main/java/cx/ring/account/JamiAccountPasswordFragment.java b/ring-android/app/src/main/java/cx/ring/account/JamiAccountPasswordFragment.java
index d818e7d23..49ff65322 100644
--- a/ring-android/app/src/main/java/cx/ring/account/JamiAccountPasswordFragment.java
+++ b/ring-android/app/src/main/java/cx/ring/account/JamiAccountPasswordFragment.java
@@ -38,7 +38,7 @@ import cx.ring.databinding.FragAccJamiPasswordBinding;
 
 import net.jami.account.JamiAccountCreationPresenter;
 import net.jami.account.JamiAccountCreationView;
-import net.jami.mvp.AccountCreationModel;
+import net.jami.model.AccountCreationModel;
 import cx.ring.mvp.BaseSupportFragment;
 import dagger.hilt.android.AndroidEntryPoint;
 
diff --git a/ring-android/app/src/main/java/cx/ring/account/JamiAccountSummaryFragment.kt b/ring-android/app/src/main/java/cx/ring/account/JamiAccountSummaryFragment.kt
index e610e253a..2b3e544c0 100644
--- a/ring-android/app/src/main/java/cx/ring/account/JamiAccountSummaryFragment.kt
+++ b/ring-android/app/src/main/java/cx/ring/account/JamiAccountSummaryFragment.kt
@@ -73,6 +73,7 @@ import io.reactivex.rxjava3.schedulers.Schedulers
 import net.jami.account.JamiAccountSummaryPresenter
 import net.jami.account.JamiAccountSummaryView
 import net.jami.model.Account
+import net.jami.model.Profile
 import net.jami.utils.StringUtils
 import java.io.File
 import java.util.*
@@ -102,10 +103,8 @@ class JamiAccountSummaryFragment :
     private var tmpProfilePhotoUri: Uri? = null
     private var mDeviceAdapter: DeviceAdapter? = null
     private val mDisposableBag = CompositeDisposable()
-    private val mProfileDisposable = CompositeDisposable()
     private var mBinding: FragAccSummaryBinding? = null
     override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
-        mDisposableBag.add(mProfileDisposable)
         return FragAccSummaryBinding.inflate(inflater, container, false).apply {
             mBinding = this
         }.root
@@ -143,9 +142,8 @@ class JamiAccountSummaryFragment :
         items.add(SettingItem(R.string.account_preferences_advanced_tab, R.drawable.round_check_circle_24) { presenter.goToAdvanced() })
         val adapter = SettingsAdapter(view.context, R.layout.item_setting, items)
         mBinding!!.settingsList.onItemClickListener =
-            AdapterView.OnItemClickListener { adapterView: AdapterView<*>?, v: View?, i: Int, l: Long ->
-                adapter.getItem(i)!!
-                    .onClick()
+            AdapterView.OnItemClickListener { _, v: View?, i: Int, l: Long ->
+                adapter.getItem(i)?.onClick()
             }
         mBinding!!.settingsList.adapter = adapter
         var totalHeight = 0
@@ -185,39 +183,30 @@ class JamiAccountSummaryFragment :
         }
     }
 
-    fun setAccount(accountId: String?) {
+    fun setAccount(accountId: String) {
         presenter.setAccountId(accountId)
     }
 
-    override fun updateUserView(account: Account) {
+    override fun updateUserView(account: Account, profile: Profile) {
         val context = context ?: return
-        mProfileDisposable.clear()
-        mProfileDisposable.add(loadProfile(context, account)
-            .observeOn(AndroidSchedulers.mainThread())
-            .subscribe({ profile ->
-                mBinding?.let { binding ->
-                    binding.userPhoto.setImageDrawable(AvatarDrawable.build(context, account, profile, true))
-                    binding.username.setText(profile.first)
-                }
-            }, { e: Throwable -> Log.e(TAG, "Error loading avatar", e) })
-        )
+        mBinding?.let { binding ->
+            binding.userPhoto.setImageDrawable(AvatarDrawable.build(context, account, profile, true))
+            binding.username.setText(profile.displayName)
+        }
     }
 
     override fun onActivityResult(requestCode: Int, resultCode: Int, resultData: Intent?) {
         when (requestCode) {
             WRITE_REQUEST_CODE -> if (resultCode == Activity.RESULT_OK) {
-                if (resultData != null) {
-                    val uri = resultData.data
-                    if (uri != null) {
-                        if (mCacheArchive != null) {
-                            AndroidFileUtils.moveToUri(requireContext().contentResolver, mCacheArchive!!, uri)
-                                .observeOn(AndroidSchedulers.mainThread())
-                                .subscribe({}) { e: Throwable ->
-                                    val v = view
-                                    if (v != null)
-                                        Snackbar.make(v, "Can't export archive: " + e.message, Snackbar.LENGTH_LONG).show()
-                                }
-                        }
+                resultData?.data?.let { uri ->
+                    mCacheArchive?.let { cacheArchive ->
+                        AndroidFileUtils.moveToUri(requireContext().contentResolver, cacheArchive, uri)
+                            .observeOn(AndroidSchedulers.mainThread())
+                            .subscribe({}) { e: Throwable ->
+                                val v = view
+                                if (v != null)
+                                    Snackbar.make(v, "Can't export archive: " + e.message, Snackbar.LENGTH_LONG).show()
+                            }
                     }
                 }
             }
@@ -226,7 +215,7 @@ class JamiAccountSummaryFragment :
                     if (resultCode == Activity.RESULT_OK) {
                         if (photoUri == null) {
                             if (resultData != null)
-                                updatePhoto(Single.just(resultData.extras!!["data"] as Bitmap?))
+                                updatePhoto(Single.just(resultData.extras!!["data"] as Bitmap))
                         } else {
                             updatePhoto(photoUri)
                         }
@@ -240,8 +229,8 @@ class JamiAccountSummaryFragment :
         }
     }
 
-    override fun accountChanged(account: Account) {
-        updateUserView(account)
+    override fun accountChanged(account: Account, profile: Profile) {
+        updateUserView(account, profile)
         mBinding?.let { binding ->
             binding.userPhoto.setOnClickListener { profileContainerClicked(account) }
             binding.linkedDevices.setText(account.deviceName)
@@ -255,7 +244,7 @@ class JamiAccountSummaryFragment :
             mBestName = "$mBestName.gz"
             val username = account.registeredName
             val currentRegisteredName = account.registeringUsername
-            val hasRegisteredName = !currentRegisteredName && username != null && !username.isEmpty()
+            val hasRegisteredName = !currentRegisteredName && username != null && username.isNotEmpty()
             binding.groupRegisteringName.visibility = if (currentRegisteredName) View.VISIBLE else View.GONE
             binding.btnShare.setOnClickListener { shareAccount(if (hasRegisteredName) username else account.username) }
             binding.registerName.visibility = if (hasRegisteredName) View.GONE else View.VISIBLE
@@ -265,11 +254,11 @@ class JamiAccountSummaryFragment :
                     .show(parentFragmentManager, QRCodeFragment.TAG)
             }
             binding.username.onFocusChangeListener = View.OnFocusChangeListener { _, hasFocus: Boolean ->
-                    val name = binding.username.text
-                    if (!hasFocus && !TextUtils.isEmpty(name)) {
-                        presenter.saveVCardFormattedName(name.toString())
-                    }
+                val name = binding.username.text
+                if (!hasFocus) {
+                    presenter.saveVCardFormattedName(name.toString())
                 }
+            }
         }
 
         setSwitchStatus(account)
@@ -671,7 +660,7 @@ class JamiAccountSummaryFragment :
         }.show(parentFragmentManager, FRAGMENT_DIALOG_RENAME)
     }
 
-    override fun onDeviceRename(newName: String?) {
+    override fun onDeviceRename(newName: String) {
         Log.d(TAG, "onDeviceRename: " + presenter.deviceName + " -> " + newName)
         presenter.renameDevice(newName)
     }
diff --git a/ring-android/app/src/main/java/cx/ring/account/JamiAccountUsernameFragment.java b/ring-android/app/src/main/java/cx/ring/account/JamiAccountUsernameFragment.java
index 5a50c8395..4ce1a6271 100644
--- a/ring-android/app/src/main/java/cx/ring/account/JamiAccountUsernameFragment.java
+++ b/ring-android/app/src/main/java/cx/ring/account/JamiAccountUsernameFragment.java
@@ -43,7 +43,7 @@ import cx.ring.databinding.FragAccJamiUsernameBinding;
 
 import net.jami.account.JamiAccountCreationPresenter;
 import net.jami.account.JamiAccountCreationView;
-import net.jami.mvp.AccountCreationModel;
+import net.jami.model.AccountCreationModel;
 import cx.ring.mvp.BaseSupportFragment;
 import cx.ring.utils.RegisteredNameFilter;
 import dagger.hilt.android.AndroidEntryPoint;
diff --git a/ring-android/app/src/main/java/cx/ring/account/JamiLinkAccountFragment.kt b/ring-android/app/src/main/java/cx/ring/account/JamiLinkAccountFragment.kt
index 34a6ccdbc..da7c68b27 100644
--- a/ring-android/app/src/main/java/cx/ring/account/JamiLinkAccountFragment.kt
+++ b/ring-android/app/src/main/java/cx/ring/account/JamiLinkAccountFragment.kt
@@ -31,7 +31,7 @@ import androidx.fragment.app.FragmentManager
 import androidx.fragment.app.FragmentStatePagerAdapter
 import androidx.viewpager.widget.ViewPager.OnPageChangeListener
 import cx.ring.databinding.FragAccJamiLinkBinding
-import net.jami.mvp.AccountCreationModel
+import net.jami.model.AccountCreationModel
 
 class JamiLinkAccountFragment : Fragment() {
     private lateinit var model: AccountCreationModel
@@ -43,7 +43,7 @@ class JamiLinkAccountFragment : Fragment() {
             override fun handleOnBackPressed() {
                 if (mCurrentFragment is ProfileCreationFragment) {
                     val fragment = mCurrentFragment as ProfileCreationFragment
-                    (activity as AccountWizardActivity?)!!.profileCreated(fragment.model, false)
+                    (activity as AccountWizardActivity?)!!.profileCreated(fragment.model!!, false)
                     return
                 }
                 mBinding!!.pager.currentItem = mBinding!!.pager.currentItem - 1
diff --git a/ring-android/app/src/main/java/cx/ring/account/JamiLinkAccountPasswordFragment.java b/ring-android/app/src/main/java/cx/ring/account/JamiLinkAccountPasswordFragment.java
index 81b175a7d..78564a78a 100644
--- a/ring-android/app/src/main/java/cx/ring/account/JamiLinkAccountPasswordFragment.java
+++ b/ring-android/app/src/main/java/cx/ring/account/JamiLinkAccountPasswordFragment.java
@@ -34,12 +34,11 @@ import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 
 import cx.ring.R;
-import cx.ring.application.JamiApplication;
 import cx.ring.databinding.FragAccJamiLinkPasswordBinding;
 
 import net.jami.account.JamiLinkAccountPresenter;
 import net.jami.account.JamiLinkAccountView;
-import net.jami.mvp.AccountCreationModel;
+import net.jami.model.AccountCreationModel;
 import cx.ring.mvp.BaseSupportFragment;
 import dagger.hilt.android.AndroidEntryPoint;
 
@@ -109,7 +108,7 @@ public class JamiLinkAccountPasswordFragment extends BaseSupportFragment<JamiLin
     }
 
     @Override
-    protected void initPresenter(net.jami.account.JamiLinkAccountPresenter presenter) {
+    protected void initPresenter(JamiLinkAccountPresenter presenter) {
         presenter.init(model);
     }
 
diff --git a/ring-android/app/src/main/java/cx/ring/account/ProfileCreationFragment.kt b/ring-android/app/src/main/java/cx/ring/account/ProfileCreationFragment.kt
index e568b8ca9..3358879f0 100644
--- a/ring-android/app/src/main/java/cx/ring/account/ProfileCreationFragment.kt
+++ b/ring-android/app/src/main/java/cx/ring/account/ProfileCreationFragment.kt
@@ -48,7 +48,7 @@ import dagger.hilt.android.AndroidEntryPoint
 import io.reactivex.rxjava3.core.Single
 import net.jami.account.ProfileCreationPresenter
 import net.jami.account.ProfileCreationView
-import net.jami.mvp.AccountCreationModel
+import net.jami.model.AccountCreationModel
 import java.io.IOException
 
 @AndroidEntryPoint
@@ -87,7 +87,7 @@ class ProfileCreationFragment : BaseSupportFragment<ProfileCreationPresenter, Pr
                     .build(view.context)
             )
         }
-        presenter.initPresenter(model)
+        presenter.initPresenter(model!!)
         binding!!.gallery.setOnClickListener { presenter.galleryClick() }
         binding!!.camera.setOnClickListener { presenter.cameraClick() }
         binding!!.nextCreateAccount.setOnClickListener { presenter.nextClick() }
@@ -197,11 +197,8 @@ class ProfileCreationFragment : BaseSupportFragment<ProfileCreationPresenter, Pr
         val newAccount = model.newAccount
         binding!!.profilePhoto.setImageDrawable(
             AvatarDrawable.Builder()
-                .withPhoto(model.photo)
-                .withNameData(
-                    accountCreationModel.getFullName(),
-                    accountCreationModel.getUsername()
-                )
+                .withPhoto(model.photo as Bitmap?)
+                .withNameData(accountCreationModel.fullName, accountCreationModel.username)
                 .withId(newAccount?.username)
                 .withCircleCrop(true)
                 .build(requireContext())
@@ -215,7 +212,7 @@ class ProfileCreationFragment : BaseSupportFragment<ProfileCreationPresenter, Pr
         const val REQUEST_PERMISSION_CAMERA = 3
         const val REQUEST_PERMISSION_READ_STORAGE = 4
 
-        fun newInstance(model: AccountCreationModelImpl?): ProfileCreationFragment {
+        fun newInstance(model: AccountCreationModelImpl): ProfileCreationFragment {
             val fragment = ProfileCreationFragment()
             fragment.model = model
             return fragment
diff --git a/ring-android/app/src/main/java/cx/ring/account/RenameDeviceDialog.kt b/ring-android/app/src/main/java/cx/ring/account/RenameDeviceDialog.kt
index 2eab16808..294d74546 100644
--- a/ring-android/app/src/main/java/cx/ring/account/RenameDeviceDialog.kt
+++ b/ring-android/app/src/main/java/cx/ring/account/RenameDeviceDialog.kt
@@ -102,7 +102,7 @@ class RenameDeviceDialog : DialogFragment() {
     }
 
     interface RenameDeviceListener {
-        fun onDeviceRename(newName: String?)
+        fun onDeviceRename(newName: String)
     }
 
     companion object {
diff --git a/ring-android/app/src/main/java/cx/ring/adapters/ConversationAdapter.kt b/ring-android/app/src/main/java/cx/ring/adapters/ConversationAdapter.kt
index 6647eaef7..ebd010d97 100644
--- a/ring-android/app/src/main/java/cx/ring/adapters/ConversationAdapter.kt
+++ b/ring-android/app/src/main/java/cx/ring/adapters/ConversationAdapter.kt
@@ -239,7 +239,6 @@ class ConversationAdapter(
                 out
             }
             Interaction.InteractionType.INVALID -> MessageType.INVALID.ordinal
-            null -> MessageType.INVALID.ordinal
         }
     }
 
@@ -258,7 +257,7 @@ class ConversationAdapter(
         val interaction = mInteractions[position]
         conversationViewHolder.compositeDisposable.clear()
         if (position > lastMsgPos) {
-            lastMsgPos = position
+            //lastMsgPos = position
             val animation = AnimationUtils.loadAnimation(conversationViewHolder.itemView.context, R.anim.fade_in)
             animation.startOffset = 150
             conversationViewHolder.itemView.startAnimation(animation)
@@ -400,7 +399,7 @@ class ConversationAdapter(
                 val options = ActivityOptionsCompat.makeSceneTransitionAnimation(conversationFragment.requireActivity(), viewHolder.mImage, "picture")
                 conversationFragment.startActivityForResult(i, 3006, options.toBundle())
             } catch (e: Exception) {
-                Log.w(TAG, "Can't open picture", e);
+                Log.w(TAG, "Can't open picture", e)
             }
         }
     }
@@ -535,23 +534,15 @@ class ConversationAdapter(
         val timeString = timestampToDetailString(viewHolder.itemView.context, file.timestamp)
         viewHolder.compositeDisposable.add(timestampUpdateTimer.subscribe {
             when (val status = file.status) {
-                InteractionStatus.TRANSFER_FINISHED -> {
-                    viewHolder.mMsgDetailTxt.text = String.format("%s - %s", timeString,
-                        Formatter.formatFileSize(viewHolder.itemView.context, file.totalSize))
-                }
-                InteractionStatus.TRANSFER_ONGOING -> {
-                    viewHolder.mMsgDetailTxt.text = String.format("%s / %s - %s",
-                        Formatter.formatFileSize(viewHolder.itemView.context, file.bytesProgress),
-                        Formatter.formatFileSize(viewHolder.itemView.context, file.totalSize),
-                        ResourceMapper.getReadableFileTransferStatus(viewHolder.itemView.context, status)
-                    )
-                }
-                else -> {
-                    viewHolder.mMsgDetailTxt.text = String.format("%s - %s - %s", timeString,
-                        Formatter.formatFileSize(viewHolder.itemView.context, file.totalSize),
-                        ResourceMapper.getReadableFileTransferStatus(viewHolder.itemView.context, status)
-                    )
-                }
+                InteractionStatus.TRANSFER_FINISHED -> viewHolder.mMsgDetailTxt.text = String.format("%s - %s", timeString,
+                    Formatter.formatFileSize(viewHolder.itemView.context, file.totalSize))
+                InteractionStatus.TRANSFER_ONGOING -> viewHolder.mMsgDetailTxt.text = String.format("%s / %s - %s",
+                    Formatter.formatFileSize(viewHolder.itemView.context, file.bytesProgress),
+                    Formatter.formatFileSize(viewHolder.itemView.context, file.totalSize),
+                    ResourceMapper.getReadableFileTransferStatus(viewHolder.itemView.context, status))
+                else -> viewHolder.mMsgDetailTxt.text = String.format("%s - %s - %s", timeString,
+                    Formatter.formatFileSize(viewHolder.itemView.context, file.totalSize),
+                    ResourceMapper.getReadableFileTransferStatus(viewHolder.itemView.context, status))
             }
         })
         val type = viewHolder.type.transferType
@@ -855,10 +846,7 @@ class ConversationAdapter(
         longPressView.setOnLongClickListener { v: View ->
             longPressView.background.setTint(conversationFragment.resources.getColor(R.color.grey_500))
             conversationFragment.updatePosition(convViewHolder.adapterPosition)
-            mCurrentLongItem = RecyclerViewContextMenuInfo(
-                convViewHolder.adapterPosition, v.id
-                    .toLong()
-            )
+            mCurrentLongItem = RecyclerViewContextMenuInfo(convViewHolder.adapterPosition, v.id.toLong())
             false
         }
         val pictureResID: Int
@@ -903,25 +891,19 @@ class ConversationAdapter(
             if (diff < DateUtils.DAY_IN_MILLIS && DateUtils.isToday(timestamp)) { // 11:32 A.M.
                 DateUtils.formatDateTime(context, timestamp, DateUtils.FORMAT_SHOW_TIME)
             } else {
-                DateUtils.formatDateTime(
-                    context, timestamp,
+                DateUtils.formatDateTime(context, timestamp,
                     DateUtils.FORMAT_SHOW_WEEKDAY or DateUtils.FORMAT_NO_YEAR or
-                            DateUtils.FORMAT_ABBREV_ALL or DateUtils.FORMAT_SHOW_TIME
-                )
+                            DateUtils.FORMAT_ABBREV_ALL or DateUtils.FORMAT_SHOW_TIME)
             }
         } else if (diff < DateUtils.YEAR_IN_MILLIS) { // JAN. 7, 11:02 A.M.
-            DateUtils.formatDateTime(
-                context, timestamp,
+            DateUtils.formatDateTime(context, timestamp,
                 DateUtils.FORMAT_SHOW_DATE or DateUtils.FORMAT_NO_YEAR or
-                        DateUtils.FORMAT_ABBREV_ALL or DateUtils.FORMAT_SHOW_TIME
-            )
+                        DateUtils.FORMAT_ABBREV_ALL or DateUtils.FORMAT_SHOW_TIME)
         } else {
-            DateUtils.formatDateTime(
-                context, timestamp,
+            DateUtils.formatDateTime(context, timestamp,
                 DateUtils.FORMAT_SHOW_TIME or DateUtils.FORMAT_SHOW_DATE or
                         DateUtils.FORMAT_SHOW_YEAR or DateUtils.FORMAT_SHOW_WEEKDAY or
-                        DateUtils.FORMAT_ABBREV_ALL
-            )
+                        DateUtils.FORMAT_ABBREV_ALL)
         }
         return timeStr.uppercase(Locale.getDefault())
     }
@@ -990,11 +972,7 @@ class ConversationAdapter(
         val nextMsg = getNextMessageFromPosition(i)
         if (prevMsg != null && nextMsg != null) {
             val nextMsgHasTime = hasPermanentTimeString(nextMsg, i + 1)
-            if ((isSeqBreak(msg, prevMsg) || isTimeShown) && !(isSeqBreak(
-                    msg,
-                    nextMsg
-                ) || nextMsgHasTime)
-            ) {
+            if ((isSeqBreak(msg, prevMsg) || isTimeShown) && !(isSeqBreak(msg, nextMsg) || nextMsgHasTime)) {
                 return SequenceType.FIRST
             } else if (!isSeqBreak(msg, prevMsg) && !isTimeShown && isSeqBreak(msg, nextMsg)) {
                 return SequenceType.LAST
@@ -1048,16 +1026,14 @@ class ConversationAdapter(
             return false
         }
         val prevMsg = getPreviousMessageFromPosition(position)
-        return prevMsg != null &&
-                msg.timestamp - prevMsg.timestamp > 10 * DateUtils.MINUTE_IN_MILLIS
+        return prevMsg != null && msg.timestamp - prevMsg.timestamp > 10 * DateUtils.MINUTE_IN_MILLIS
     }
 
     private fun lastOutgoingIndex(): Int {
         var i: Int = mInteractions.size - 1
         while (i >= 0) {
-            if (!mInteractions[i].isIncoming) {
+            if (!mInteractions[i].isIncoming)
                 break
-            }
             i--
         }
         return i
@@ -1123,19 +1099,14 @@ class ConversationAdapter(
         val res = conversationFragment.resources
         hPadding = res.getDimensionPixelSize(R.dimen.padding_medium)
         vPadding = res.getDimensionPixelSize(R.dimen.padding_small)
-        mPictureMaxSize = TypedValue.applyDimension(
-            TypedValue.COMPLEX_UNIT_DIP,
-            200f,
-            res.displayMetrics
-        ).toInt()
+        mPictureMaxSize = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 200f, res.displayMetrics).toInt()
         val corner = res.getDimension(R.dimen.conversation_message_radius).toInt()
         PICTURE_OPTIONS = GlideOptions()
             .transform(CenterInside())
             .fitCenter()
             .override(mPictureMaxSize)
             .transform(RoundedCorners(corner))
-        timestampUpdateTimer =
-            Observable.interval(10, TimeUnit.SECONDS, AndroidSchedulers.mainThread())
-                .startWithItem(0L)
+        timestampUpdateTimer = Observable.interval(10, TimeUnit.SECONDS, AndroidSchedulers.mainThread())
+            .startWithItem(0L)
     }
 }
\ No newline at end of file
diff --git a/ring-android/app/src/main/java/cx/ring/adapters/RingtoneAdapter.java b/ring-android/app/src/main/java/cx/ring/adapters/RingtoneAdapter.java
deleted file mode 100644
index a22acb9c6..000000000
--- a/ring-android/app/src/main/java/cx/ring/adapters/RingtoneAdapter.java
+++ /dev/null
@@ -1,175 +0,0 @@
-/*
- *  Copyright (C) 2004-2021 Savoir-faire Linux Inc.
- *
- *  Authors: Rayan Osseiran <rayan.osseiran@savoirfairelinux.com>
- *
- *  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, write to the Free Software
- *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-package cx.ring.adapters;
-
-import android.graphics.drawable.Drawable;
-import android.media.MediaPlayer;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.ImageView;
-import android.widget.TextView;
-
-import androidx.annotation.NonNull;
-import androidx.recyclerview.widget.RecyclerView;
-
-import com.bumptech.glide.Glide;
-import com.bumptech.glide.request.target.DrawableImageViewTarget;
-
-import java.io.IOException;
-import java.util.List;
-
-import cx.ring.R;
-import io.reactivex.rxjava3.core.Observable;
-import io.reactivex.rxjava3.subjects.PublishSubject;
-import io.reactivex.rxjava3.subjects.Subject;
-
-import net.jami.model.Ringtone;
-import net.jami.utils.Log;
-
-public class RingtoneAdapter extends RecyclerView.Adapter<RingtoneAdapter.RingtoneViewHolder> {
-
-    private final String TAG = RingtoneAdapter.class.getSimpleName();
-    private final List<Ringtone> ringtoneList;
-    private int currentlySelectedPosition = 1; // default item
-    private final MediaPlayer mp = new MediaPlayer();
-    private final Subject<Ringtone> ringtoneSubject = PublishSubject.create();
-
-    static class RingtoneViewHolder extends RecyclerView.ViewHolder {
-        private final TextView name;
-        private final ImageView isSelected, isPlaying, ringtoneIcon;
-
-        RingtoneViewHolder(View view) {
-            super(view);
-            name = view.findViewById(R.id.item_ringtone_name);
-            isSelected = view.findViewById(R.id.item_ringtone_selected);
-            isPlaying = view.findViewById(R.id.item_ringtone_playing);
-            ringtoneIcon = view.findViewById(R.id.item_ringtone_icon);
-            Glide.with(view.getContext())
-                    .load(R.raw.baseline_graphic_eq_black_24dp)
-                    .placeholder(R.drawable.baseline_graphic_eq_24)
-                    .into(new DrawableImageViewTarget(isPlaying));
-        }
-    }
-
-
-    public RingtoneAdapter(List<Ringtone> ringtones) {
-        ringtoneList = ringtones;
-    }
-
-    @Override
-    @NonNull
-    public RingtoneViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
-        RingtoneViewHolder viewHolder = new RingtoneViewHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.item_ringtone, parent, false));
-        configureRingtoneView(viewHolder);
-        return viewHolder;
-    }
-
-    @Override
-    public void onBindViewHolder(@NonNull RingtoneViewHolder holder, int position) {
-        final Ringtone ringtone = ringtoneList.get(position);
-        holder.name.setText(ringtone.getName());
-        holder.ringtoneIcon.setImageDrawable((Drawable) ringtone.getRingtoneIcon());
-        holder.isSelected.setVisibility((ringtone.isSelected() ? View.VISIBLE : View.INVISIBLE));
-        holder.isPlaying.setVisibility((ringtone.isPlaying() ? View.VISIBLE : View.INVISIBLE));
-    }
-
-    @Override
-    public int getItemCount() {
-        return ringtoneList.size();
-    }
-
-    private void configureRingtoneView(RingtoneViewHolder viewHolder) {
-        viewHolder.itemView.setOnClickListener(view -> {
-            if (currentlySelectedPosition == viewHolder.getAdapterPosition() && mp.isPlaying()) {
-                stopPreview();
-                return;
-            } else {
-                resetState();
-            }
-
-            currentlySelectedPosition = viewHolder.getAdapterPosition();
-            Ringtone ringtone = ringtoneList.get(currentlySelectedPosition);
-            try {
-                mp.setDataSource(ringtone.getRingtonePath());
-                mp.prepare();
-                mp.start();
-                ringtone.setPlaying(true);
-            } catch (IOException | IllegalStateException | NullPointerException e) {
-                stopPreview();
-                Log.e(TAG, "Error previewing ringtone", e);
-            } finally {
-                ringtoneSubject.onNext(ringtone);
-                ringtone.setSelected(true);
-                notifyItemChanged(currentlySelectedPosition);
-            }
-            mp.setOnCompletionListener(mp ->
-                    stopPreview());
-        });
-    }
-
-    /**
-     * Stops the preview from playing and disables the playing animation
-     */
-    public void stopPreview() {
-        if(mp.isPlaying())
-            mp.stop();
-        mp.reset();
-        ringtoneList.get(currentlySelectedPosition).setPlaying(false);
-        notifyItemChanged(currentlySelectedPosition);
-    }
-
-    public void releaseMediaPlayer() {
-        mp.release();
-    }
-
-    /**
-     * Deselects the current item and stops the preview
-     */
-    public void resetState() {
-        ringtoneList.get(currentlySelectedPosition).setSelected(false);
-        stopPreview();
-    }
-
-    /**
-     * Sets the ringtone from the user settings
-     * @param path the ringtone path
-     * @param enabled true if the user did not select silent
-     */
-    public void selectDefaultItem(String path, boolean enabled) {
-        if (!enabled) {
-            currentlySelectedPosition = 0;
-        } else {
-            // ignore first element because it represents silent and has a null path (checked for before)
-            for (int ringtoneIndex = 1; ringtoneIndex < ringtoneList.size(); ringtoneIndex++) {
-                if (ringtoneList.get(ringtoneIndex).getRingtonePath().equals(path)) {
-                    currentlySelectedPosition = ringtoneIndex;
-                }
-            }
-        }
-        ringtoneList.get(currentlySelectedPosition).setSelected(true);
-        notifyItemChanged(currentlySelectedPosition);
-    }
-
-    public Observable<Ringtone> getRingtoneSubject() {
-        return ringtoneSubject;
-    }
-
-}
diff --git a/ring-android/app/src/main/java/cx/ring/adapters/RingtoneAdapter.kt b/ring-android/app/src/main/java/cx/ring/adapters/RingtoneAdapter.kt
new file mode 100644
index 000000000..b02092568
--- /dev/null
+++ b/ring-android/app/src/main/java/cx/ring/adapters/RingtoneAdapter.kt
@@ -0,0 +1,160 @@
+/*
+ *  Copyright (C) 2004-2021 Savoir-faire Linux Inc.
+ *
+ *  Authors: Rayan Osseiran <rayan.osseiran@savoirfairelinux.com>
+ *
+ *  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, write to the Free Software
+ *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+package cx.ring.adapters
+
+import android.graphics.drawable.Drawable
+import android.media.MediaPlayer
+import android.util.Log
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import android.widget.ImageView
+import android.widget.TextView
+import androidx.recyclerview.widget.RecyclerView
+import com.bumptech.glide.Glide
+import com.bumptech.glide.request.target.DrawableImageViewTarget
+import cx.ring.R
+import cx.ring.adapters.RingtoneAdapter.RingtoneViewHolder
+import io.reactivex.rxjava3.core.Observable
+import io.reactivex.rxjava3.subjects.PublishSubject
+import io.reactivex.rxjava3.subjects.Subject
+import net.jami.model.Ringtone
+import java.io.IOException
+
+class RingtoneAdapter(private val ringtoneList: List<Ringtone>) : RecyclerView.Adapter<RingtoneViewHolder>() {
+    private var currentlySelectedPosition = 1 // default item
+    private val mp = MediaPlayer()
+    private val ringtoneSubject: Subject<Ringtone> = PublishSubject.create()
+
+    class RingtoneViewHolder(view: View) : RecyclerView.ViewHolder(view) {
+        val name: TextView = view.findViewById(R.id.item_ringtone_name)
+        val isSelected: ImageView = view.findViewById(R.id.item_ringtone_selected)
+        val isPlaying: ImageView = view.findViewById(R.id.item_ringtone_playing)
+        val ringtoneIcon: ImageView = view.findViewById(R.id.item_ringtone_icon)
+
+        init {
+            Glide.with(view.context)
+                .load(R.raw.baseline_graphic_eq_black_24dp)
+                .placeholder(R.drawable.baseline_graphic_eq_24)
+                .into(DrawableImageViewTarget(isPlaying))
+        }
+    }
+
+    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RingtoneViewHolder {
+        val viewHolder = RingtoneViewHolder(LayoutInflater.from(parent.context).inflate(R.layout.item_ringtone, parent, false))
+        configureRingtoneView(viewHolder)
+        return viewHolder
+    }
+
+    override fun onBindViewHolder(holder: RingtoneViewHolder, position: Int) {
+        val ringtone = ringtoneList[position]
+        holder.name.text = ringtone.name
+        holder.ringtoneIcon.setImageDrawable(ringtone.ringtoneIcon as Drawable)
+        holder.isSelected.visibility = if (ringtone.isSelected) View.VISIBLE else View.INVISIBLE
+        holder.isPlaying.visibility = if (ringtone.isPlaying) View.VISIBLE else View.INVISIBLE
+    }
+
+    override fun getItemCount(): Int {
+        return ringtoneList.size
+    }
+
+    private fun configureRingtoneView(viewHolder: RingtoneViewHolder) {
+        viewHolder.itemView.setOnClickListener {
+            if (currentlySelectedPosition == viewHolder.adapterPosition && mp.isPlaying) {
+                stopPreview()
+                return@setOnClickListener
+            } else {
+                resetState()
+            }
+            currentlySelectedPosition = viewHolder.adapterPosition
+            val ringtone = ringtoneList[currentlySelectedPosition]
+            try {
+                mp.setDataSource(ringtone.ringtonePath)
+                mp.prepare()
+                mp.start()
+                ringtone.isPlaying = true
+            } catch (e: IOException) {
+                stopPreview()
+                Log.e(TAG, "Error previewing ringtone", e)
+            } catch (e: IllegalStateException) {
+                stopPreview()
+                Log.e(TAG, "Error previewing ringtone", e)
+            } catch (e: NullPointerException) {
+                stopPreview()
+                Log.e(TAG, "Error previewing ringtone", e)
+            } finally {
+                ringtoneSubject.onNext(ringtone)
+                ringtone.isSelected = true
+                notifyItemChanged(currentlySelectedPosition)
+            }
+            mp.setOnCompletionListener { stopPreview() }
+        }
+    }
+
+    /**
+     * Stops the preview from playing and disables the playing animation
+     */
+    private fun stopPreview() {
+        if (mp.isPlaying) mp.stop()
+        mp.reset()
+        ringtoneList[currentlySelectedPosition].isPlaying = false
+        notifyItemChanged(currentlySelectedPosition)
+    }
+
+    fun releaseMediaPlayer() {
+        mp.release()
+    }
+
+    /**
+     * Deselects the current item and stops the preview
+     */
+    fun resetState() {
+        ringtoneList[currentlySelectedPosition].isSelected = false
+        stopPreview()
+    }
+
+    /**
+     * Sets the ringtone from the user settings
+     * @param path the ringtone path
+     * @param enabled true if the user did not select silent
+     */
+    fun selectDefaultItem(path: String, enabled: Boolean) {
+        if (!enabled) {
+            currentlySelectedPosition = 0
+        } else {
+            // ignore first element because it represents silent and has a null path (checked for before)
+            for (ringtoneIndex in 1 until ringtoneList.size) {
+                if (ringtoneList[ringtoneIndex].ringtonePath == path) {
+                    currentlySelectedPosition = ringtoneIndex
+                }
+            }
+        }
+        ringtoneList[currentlySelectedPosition].isSelected = true
+        notifyItemChanged(currentlySelectedPosition)
+    }
+
+    fun getRingtone(): Observable<Ringtone> {
+        return ringtoneSubject
+    }
+
+    companion object {
+        private val TAG = RingtoneAdapter::class.simpleName!!
+    }
+}
\ No newline at end of file
diff --git a/ring-android/app/src/main/java/cx/ring/client/AccountSpinnerAdapter.kt b/ring-android/app/src/main/java/cx/ring/client/AccountSpinnerAdapter.kt
index 4565d7ed3..28b16a428 100644
--- a/ring-android/app/src/main/java/cx/ring/client/AccountSpinnerAdapter.kt
+++ b/ring-android/app/src/main/java/cx/ring/client/AccountSpinnerAdapter.kt
@@ -30,11 +30,12 @@ import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
 import android.widget.RelativeLayout
 import cx.ring.databinding.ItemToolbarSelectedBinding
 import cx.ring.databinding.ItemToolbarSpinnerBinding
+import cx.ring.services.VCardServiceImpl
 import io.reactivex.rxjava3.disposables.CompositeDisposable
-import io.reactivex.rxjava3.schedulers.Schedulers
 import net.jami.model.Account
+import net.jami.model.Profile
 
-class AccountSpinnerAdapter(context: Context, accounts: List<Account>) :
+class AccountSpinnerAdapter(context: Context, accounts: List<Account>, val disposable: CompositeDisposable) :
     ArrayAdapter<Account>(context, R.layout.item_toolbar_spinner, accounts) {
     private val mInflater: LayoutInflater = LayoutInflater.from(context)
     private val logoSize: Int = context.resources.getDimensionPixelSize(R.dimen.list_medium_icon_size)
@@ -44,7 +45,7 @@ class AccountSpinnerAdapter(context: Context, accounts: List<Account>) :
         val type = getItemViewType(position)
         val holder: ViewHolderHeader
         if (view == null) {
-            holder = ViewHolderHeader(ItemToolbarSelectedBinding.inflate(mInflater, parent, false))
+            holder = ViewHolderHeader(ItemToolbarSelectedBinding.inflate(mInflater, parent, false), disposable)
             view = holder.binding.root
             view.setTag(holder)
         } else {
@@ -53,14 +54,13 @@ class AccountSpinnerAdapter(context: Context, accounts: List<Account>) :
         }
         if (type == TYPE_ACCOUNT) {
             val account = getItem(position)!!
-            holder.loader.add(AvatarDrawable.load(context, account)
+            holder.loader.add(VCardServiceImpl.loadProfile(context, account)
+                .map { profile -> Profile(profile.displayName ?: account.alias, AvatarDrawable.build(context, account, profile)) }
                 .observeOn(AndroidSchedulers.mainThread())
-                .subscribe({ avatar -> holder.binding.logo.setImageDrawable(avatar)
-                }) { e: Throwable -> Log.e(TAG, "Error loading avatar", e) })
-            holder.loader.add(account.accountAlias
-                    .observeOn(AndroidSchedulers.mainThread())
-                    .subscribe({ alias -> holder.binding.title.text = alias.ifEmpty { context.getString(R.string.ring_account) }
-                    }) { e: Throwable -> Log.e(TAG, "Error loading title", e) })
+                .subscribe({ profile ->
+                    holder.binding.logo.setImageDrawable(profile.avatar as AvatarDrawable)
+                    holder.binding.title.text = profile.displayName?.ifEmpty { context.getString(R.string.ring_account) }
+                }){ e: Throwable -> Log.e(TAG, "Error loading avatar", e) })
         }
         return view
     }
@@ -70,7 +70,7 @@ class AccountSpinnerAdapter(context: Context, accounts: List<Account>) :
         val holder: ViewHolder
         var rowView = convertView
         if (rowView == null) {
-            holder = ViewHolder(ItemToolbarSpinnerBinding.inflate(mInflater, parent, false))
+            holder = ViewHolder(ItemToolbarSpinnerBinding.inflate(mInflater, parent, false), disposable)
             rowView = holder.binding.root
             rowView.setTag(holder)
         } else {
@@ -82,33 +82,29 @@ class AccountSpinnerAdapter(context: Context, accounts: List<Account>) :
         if (type == TYPE_ACCOUNT) {
             val account = getItem(position)!!
             val ip2ipString = rowView.context.getString(R.string.account_type_ip2ip)
-            holder.loader.add(account.accountAlias
-                    .observeOn(AndroidSchedulers.mainThread())
-                    .subscribe { alias ->
-                        val subtitle = getUri(account, ip2ipString)
-                        holder.binding.title.text = alias.ifEmpty { context.getString(R.string.ring_account) }
-                        if (alias == subtitle) {
-                            holder.binding.subtitle.visibility = View.GONE
-                        } else {
-                            holder.binding.subtitle.visibility = View.VISIBLE
-                            holder.binding.subtitle.text = subtitle
-                        }
-                    })
             val params = holder.binding.title.layoutParams as RelativeLayout.LayoutParams
             params.removeRule(RelativeLayout.CENTER_VERTICAL)
             holder.binding.title.layoutParams = params
             logoParam.width = logoSize
             logoParam.height = logoSize
             holder.binding.logo.layoutParams = logoParam
-            holder.loader.add(AvatarDrawable.load(context, account)
-                .subscribeOn(Schedulers.io())
+            holder.loader.add(VCardServiceImpl.loadProfile(context, account)
+                .map { profile -> Profile(profile.displayName ?: account.alias, AvatarDrawable.build(context, account, profile)) }
                 .observeOn(AndroidSchedulers.mainThread())
-                .subscribe({ avatar -> holder.binding.logo.setImageDrawable(avatar) })
-                { e -> Log.e(TAG, "Error loading avatar", e) })
+                .subscribe({ profile ->
+                    val subtitle = getUri(account, ip2ipString)
+                    holder.binding.logo.setImageDrawable(profile.avatar as AvatarDrawable)
+                    holder.binding.title.text = profile.displayName?.ifEmpty { context.getString(R.string.ring_account) }
+                    if (profile.displayName == subtitle) {
+                        holder.binding.subtitle.visibility = View.GONE
+                    } else {
+                        holder.binding.subtitle.visibility = View.VISIBLE
+                        holder.binding.subtitle.text = subtitle
+                    }
+                }){ e: Throwable -> Log.e(TAG, "Error loading avatar", e) })
         } else {
             holder.binding.title.setText(
                 if (type == TYPE_CREATE_JAMI) R.string.add_ring_account_title else R.string.add_sip_account_title)
-
             holder.binding.subtitle.visibility = View.GONE
             holder.binding.logo.setImageResource(R.drawable.baseline_add_24)
             logoParam.width = ViewGroup.LayoutParams.WRAP_CONTENT
@@ -134,12 +130,12 @@ class AccountSpinnerAdapter(context: Context, accounts: List<Account>) :
         return super.getCount() + 2
     }
 
-    private class ViewHolder(val binding: ItemToolbarSpinnerBinding) {
-        val loader = CompositeDisposable()
+    private class ViewHolder(val binding: ItemToolbarSpinnerBinding, parentDisposable: CompositeDisposable) {
+        val loader = CompositeDisposable().apply { parentDisposable.add(this) }
     }
 
-    private class ViewHolderHeader(val binding: ItemToolbarSelectedBinding) {
-        val loader = CompositeDisposable()
+    private class ViewHolderHeader(val binding: ItemToolbarSelectedBinding, parentDisposable: CompositeDisposable) {
+        val loader = CompositeDisposable().apply { parentDisposable.add(this) }
     }
 
     private fun getUri(account: Account, defaultNameSip: CharSequence): String {
diff --git a/ring-android/app/src/main/java/cx/ring/client/ContactDetailsActivity.kt b/ring-android/app/src/main/java/cx/ring/client/ContactDetailsActivity.kt
index 681b9b4f5..3b79991a9 100644
--- a/ring-android/app/src/main/java/cx/ring/client/ContactDetailsActivity.kt
+++ b/ring-android/app/src/main/java/cx/ring/client/ContactDetailsActivity.kt
@@ -38,6 +38,7 @@ import androidx.recyclerview.widget.RecyclerView
 import com.google.android.material.dialog.MaterialAlertDialogBuilder
 import com.google.android.material.snackbar.Snackbar
 import cx.ring.R
+import cx.ring.application.JamiApplication
 import cx.ring.client.ColorChooserBottomSheet.IColorSelected
 import cx.ring.client.EmojiChooserBottomSheet.IEmojiSelected
 import cx.ring.databinding.ActivityContactDetailsBinding
@@ -116,9 +117,7 @@ class ContactDetailsActivity : AppCompatActivity() {
     internal class ContactActionView(
         val binding: ItemContactActionBinding,
         parentDisposable: CompositeDisposable
-    ) : RecyclerView.ViewHolder(
-        binding.root
-    ) {
+    ) : RecyclerView.ViewHolder(binding.root) {
         var callback: (() -> Unit)? = null
         val disposable = CompositeDisposable()
 
@@ -240,6 +239,7 @@ class ContactDetailsActivity : AppCompatActivity() {
             finish()
             return
         }
+        JamiApplication.instance?.startDaemon()
         binding = ActivityContactDetailsBinding.inflate(layoutInflater)
         setContentView(binding!!.root)
         //JamiApplication.getInstance().getInjectionComponent().inject(this);
@@ -285,11 +285,7 @@ class ContactDetailsActivity : AppCompatActivity() {
         //adapter.actions.add(new ContactAction(R.drawable.baseline_info_24, getText(infoString), () -> {}));
         binding!!.conversationType.setText(infoString)
         //binding.conversationType.setCompoundDrawables(getDrawable(infoIcon), null, null, null);
-        colorAction = ContactAction(
-            R.drawable.item_color_background,
-            0,
-            getText(R.string.conversation_preference_color)
-        ) {
+        colorAction = ContactAction(R.drawable.item_color_background, 0, getText(R.string.conversation_preference_color)) {
             val frag = ColorChooserBottomSheet()
             frag.setCallback(object : IColorSelected {
                 override fun onColorSelected(color: Int) {
diff --git a/ring-android/app/src/main/java/cx/ring/client/ConversationActivity.kt b/ring-android/app/src/main/java/cx/ring/client/ConversationActivity.kt
index c0e072437..6427e7b2f 100644
--- a/ring-android/app/src/main/java/cx/ring/client/ConversationActivity.kt
+++ b/ring-android/app/src/main/java/cx/ring/client/ConversationActivity.kt
@@ -58,7 +58,7 @@ class ConversationActivity : AppCompatActivity(), Colorable {
         }
         conversationPath = path;
         val isBubble = getIntent().getBooleanExtra(NotificationServiceImpl.EXTRA_BUBBLE, false)
-        JamiApplication.instance!!.startDaemon()
+        JamiApplication.instance?.startDaemon()
         val binding = ActivityConversationBinding.inflate(layoutInflater)
         setContentView(binding.root)
         setSupportActionBar(binding.mainToolbar)
diff --git a/ring-android/app/src/main/java/cx/ring/client/ConversationSelectionActivity.kt b/ring-android/app/src/main/java/cx/ring/client/ConversationSelectionActivity.kt
index 801864f84..9176a1231 100644
--- a/ring-android/app/src/main/java/cx/ring/client/ConversationSelectionActivity.kt
+++ b/ring-android/app/src/main/java/cx/ring/client/ConversationSelectionActivity.kt
@@ -20,12 +20,12 @@ package cx.ring.client
 
 import android.content.Intent
 import android.os.Bundle
-import android.text.TextUtils
 import androidx.appcompat.app.AppCompatActivity
 import androidx.recyclerview.widget.LinearLayoutManager
 import androidx.recyclerview.widget.RecyclerView
 import cx.ring.R
 import cx.ring.adapters.SmartListAdapter
+import cx.ring.application.JamiApplication
 import cx.ring.fragments.CallFragment
 import cx.ring.utils.ConversationPath
 import cx.ring.viewholders.SmartListViewHolder.SmartListListeners
@@ -53,14 +53,14 @@ class ConversationSelectionActivity : AppCompatActivity() {
     var mCallService: CallService
 
     private val adapter: SmartListAdapter = SmartListAdapter(null, object : SmartListListeners {
-        override fun onItemClick(smartListViewModel: SmartListViewModel) {
+        override fun onItemClick(item: SmartListViewModel) {
             val intent = Intent()
-            intent.data = ConversationPath.toUri(smartListViewModel.accountId, smartListViewModel.uri)
+            intent.data = ConversationPath.toUri(item.accountId, item.uri)
             setResult(RESULT_OK, intent)
             finish()
         }
 
-        override fun onItemLongClick(smartListViewModel: SmartListViewModel) {}
+        override fun onItemLongClick(item: SmartListViewModel) {}
     }, mDisposable)
 
     override fun onCreate(savedInstanceState: Bundle?) {
@@ -69,6 +69,7 @@ class ConversationSelectionActivity : AppCompatActivity() {
         val list = findViewById<RecyclerView>(R.id.conversationList)
         list.layoutManager = LinearLayoutManager(this)
         list.adapter = adapter
+        JamiApplication.instance?.startDaemon()
     }
 
     public override fun onStart() {
diff --git a/ring-android/app/src/main/java/cx/ring/client/HomeActivity.kt b/ring-android/app/src/main/java/cx/ring/client/HomeActivity.kt
index 40643d7b6..dafd6843d 100644
--- a/ring-android/app/src/main/java/cx/ring/client/HomeActivity.kt
+++ b/ring-android/app/src/main/java/cx/ring/client/HomeActivity.kt
@@ -169,11 +169,8 @@ class HomeActivity : AppCompatActivity(), NavigationBarView.OnItemSelectedListen
         val extra = intent.extras
         val action = intent.action
         if (ACTION_PRESENT_TRUST_REQUEST_FRAGMENT == action) {
-            if (extra?.getString(ContactRequestsFragment.ACCOUNT_ID) == null) {
-                return
-            }
             //mAccountWithPendingrequests = extra.getString(ContactRequestsFragment.ACCOUNT_ID);
-            presentTrustRequestFragment(extra.getString(ContactRequestsFragment.ACCOUNT_ID))
+            presentTrustRequestFragment(extra?.getString(ContactRequestsFragment.ACCOUNT_ID) ?: return)
         } else if (Intent.ACTION_SEND == action || Intent.ACTION_SEND_MULTIPLE == action) {
             val path = ConversationPath.fromBundle(extra)
             if (path != null) {
@@ -261,7 +258,7 @@ class HomeActivity : AppCompatActivity(), NavigationBarView.OnItemSelectedListen
                 .observeOn(AndroidSchedulers.mainThread())
                 .subscribe({ accounts ->
                     if (mAccountAdapter == null) {
-                        mAccountAdapter = AccountSpinnerAdapter(this@HomeActivity, ArrayList(accounts)).apply {
+                        mAccountAdapter = AccountSpinnerAdapter(this@HomeActivity, ArrayList(accounts), mDisposable).apply {
                             setNotifyOnChange(false)
                             mBinding?.spinnerToolbar?.adapter = this
                         }
@@ -375,7 +372,7 @@ class HomeActivity : AppCompatActivity(), NavigationBarView.OnItemSelectedListen
             .commit()
     }
 
-    private fun presentTrustRequestFragment(accountID: String?) {
+    private fun presentTrustRequestFragment(accountID: String) {
         mNotificationService.cancelTrustRequestNotification(accountID)
         if (fContent is ContactRequestsFragment) {
             (fContent as ContactRequestsFragment).presentForAccount(accountID)
@@ -635,7 +632,7 @@ class HomeActivity : AppCompatActivity(), NavigationBarView.OnItemSelectedListen
      * Changes the current main fragment to a plugin settings fragment
      * @param pluginDetails
      */
-    fun gotToPluginSettings(pluginDetails: PluginDetails?) {
+    fun gotToPluginSettings(pluginDetails: PluginDetails) {
         if (fContent is PluginSettingsFragment) {
             return
         }
@@ -651,7 +648,7 @@ class HomeActivity : AppCompatActivity(), NavigationBarView.OnItemSelectedListen
     /**
      * Changes the current main fragment to a plugin PATH preference fragment
      */
-    fun gotToPluginPathPreference(pluginDetails: PluginDetails?, preferenceKey: String?) {
+    fun gotToPluginPathPreference(pluginDetails: PluginDetails, preferenceKey: String) {
         if (fContent is PluginPathPreferenceFragment) {
             return
         }
diff --git a/ring-android/app/src/main/java/cx/ring/client/MediaViewerFragment.kt b/ring-android/app/src/main/java/cx/ring/client/MediaViewerFragment.kt
index 77c2d8271..da2ef8bc1 100644
--- a/ring-android/app/src/main/java/cx/ring/client/MediaViewerFragment.kt
+++ b/ring-android/app/src/main/java/cx/ring/client/MediaViewerFragment.kt
@@ -38,11 +38,7 @@ class MediaViewerFragment : Fragment() {
     private var mUri: Uri? = null
     private var mImage: ImageView? = null
 
-    override fun onCreateView(
-        inflater: LayoutInflater,
-        container: ViewGroup?,
-        savedInstanceState: Bundle?
-    ): View {
+    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
         val view = inflater.inflate(R.layout.fragment_media_viewer, container, false) as ViewGroup
         mImage = view.findViewById(R.id.image)
         showImage()
diff --git a/ring-android/app/src/main/java/cx/ring/client/RingtoneActivity.kt b/ring-android/app/src/main/java/cx/ring/client/RingtoneActivity.kt
index e604f5a7c..fb12c01ae 100644
--- a/ring-android/app/src/main/java/cx/ring/client/RingtoneActivity.kt
+++ b/ring-android/app/src/main/java/cx/ring/client/RingtoneActivity.kt
@@ -24,6 +24,7 @@ import android.content.DialogInterface
 import android.content.Intent
 import android.media.MediaPlayer
 import android.os.Bundle
+import android.util.Log
 import android.view.View
 import android.widget.ImageView
 import android.widget.TextView
@@ -48,7 +49,6 @@ import net.jami.model.Account
 import net.jami.model.ConfigKey
 import net.jami.model.Ringtone
 import net.jami.services.AccountService
-import net.jami.utils.Log
 import java.io.File
 import java.io.IOException
 import java.util.*
@@ -57,11 +57,11 @@ import javax.inject.Singleton
 
 @AndroidEntryPoint
 class RingtoneActivity : AppCompatActivity() {
-    private var adapter: RingtoneAdapter? = null
+    private lateinit var adapter: RingtoneAdapter
     private lateinit var mAccount: Account
-    private var customRingtone: TextView? = null
-    private var customPlaying: ImageView? = null
-    private var customSelected: ImageView? = null
+    private lateinit var customRingtone: TextView
+    private lateinit var customPlaying: ImageView
+    private lateinit var customSelected: ImageView
     private val mediaPlayer: MediaPlayer = MediaPlayer()
     private var disposable: Disposable? = null
 
@@ -78,28 +78,27 @@ class RingtoneActivity : AppCompatActivity() {
             return
         }
         mAccount = account
-
-        /*Toolbar toolbar = findViewById(R.id.ringtoneToolbar);
-        toolbar.setNavigationOnClickListener(view -> finish());*/
-        val recycler = findViewById<RecyclerView>(R.id.ringToneRecycler)
-        val customRingtoneLayout = findViewById<ConstraintLayout>(R.id.customRingtoneLayout)
         customRingtone = findViewById(R.id.customRingtoneName)
         customPlaying = findViewById(R.id.custom_ringtone_playing)
         customSelected = findViewById(R.id.custom_ringtone_selected)
         adapter = RingtoneAdapter(prepareRingtones())
         val upcomingLayoutManager: LayoutManager = LinearLayoutManager(this)
+
+        val recycler = findViewById<RecyclerView>(R.id.ringToneRecycler)
         recycler.layoutManager = upcomingLayoutManager
         recycler.itemAnimator = DefaultItemAnimator()
         recycler.adapter = adapter
 
         // loads the user's settings
         setPreference()
+
+        val customRingtoneLayout = findViewById<ConstraintLayout>(R.id.customRingtoneLayout)
         customRingtoneLayout.setOnClickListener { displayFileSearchDialog() }
         customRingtoneLayout.setOnLongClickListener {
             displayRemoveDialog()
             true
         }
-        disposable = adapter!!.ringtoneSubject.subscribe({ ringtone: Ringtone ->
+        disposable = adapter.getRingtone().subscribe({ ringtone: Ringtone ->
             setJamiRingtone(ringtone)
             removeCustomRingtone()
         }) { Log.e(TAG, "Error updating ringtone status") }
@@ -117,18 +116,17 @@ class RingtoneActivity : AppCompatActivity() {
 
     override fun finish() {
         super.finish()
-        adapter!!.releaseMediaPlayer()
+        adapter.releaseMediaPlayer()
         mediaPlayer.release()
     }
 
     private fun prepareRingtones(): List<Ringtone> {
         val ringtoneList: MutableList<Ringtone> = ArrayList()
         val ringtoneFolder = File(filesDir, "ringtones")
-        val ringtones = ringtoneFolder.listFiles()
-        val ringtoneIcon = getDrawable(R.drawable.baseline_notifications_active_24)
-        if (ringtones == null) return ringtoneList
+        val ringtones = ringtoneFolder.listFiles() ?: return emptyList()
+        val ringtoneIcon = getDrawable(R.drawable.baseline_notifications_active_24)!!
         Arrays.sort(ringtones) { a: File, b: File -> a.name.compareTo(b.name) }
-        ringtoneList.add(Ringtone("Silent", null, getDrawable(R.drawable.baseline_notifications_off_24)))
+        ringtoneList.add(Ringtone("Silent", null, getDrawable(R.drawable.baseline_notifications_off_24)!!))
         for (file in ringtones) {
             val name = stripFileNameExtension(file.name)
             ringtoneList.add(Ringtone(name, file.absolutePath, ringtoneIcon))
@@ -143,10 +141,10 @@ class RingtoneActivity : AppCompatActivity() {
         val path = File(mAccount.config[ConfigKey.RINGTONE_PATH])
         val customEnabled = mAccount.config.getBool(ConfigKey.RINGTONE_CUSTOM)
         if (customEnabled && path.exists()) {
-            customRingtone!!.text = path.name
-            customSelected!!.visibility = View.VISIBLE
+            customRingtone.text = path.name
+            customSelected.visibility = View.VISIBLE
         } else if (path.exists()) {
-            adapter!!.selectDefaultItem(path.absolutePath, mAccount.config.getBool(ConfigKey.RINGTONE_ENABLED))
+            adapter.selectDefaultItem(path.absolutePath, mAccount.config.getBool(ConfigKey.RINGTONE_ENABLED))
         } else {
             setDefaultRingtone()
         }
@@ -155,7 +153,7 @@ class RingtoneActivity : AppCompatActivity() {
     private fun setDefaultRingtone() {
         val ringtonesDir = File(filesDir, "ringtones")
         val ringtonePath = File(ringtonesDir, getString(R.string.ringtone_default_name)).absolutePath
-        adapter!!.selectDefaultItem(ringtonePath, mAccount.config.getBool(ConfigKey.RINGTONE_ENABLED))
+        adapter.selectDefaultItem(ringtonePath, mAccount.config.getBool(ConfigKey.RINGTONE_ENABLED))
         mAccount.setDetail(ConfigKey.RINGTONE_PATH, ringtonePath)
         mAccount.setDetail(ConfigKey.RINGTONE_CUSTOM, false)
         updateAccount()
@@ -225,9 +223,9 @@ class RingtoneActivity : AppCompatActivity() {
      * Removes a custom ringtone and updates the view
      */
     private fun removeCustomRingtone() {
-        customSelected!!.visibility = View.INVISIBLE
-        customPlaying!!.visibility = View.INVISIBLE
-        customRingtone!!.setText(R.string.ringtone_custom_prompt)
+        customSelected.visibility = View.INVISIBLE
+        customPlaying.visibility = View.INVISIBLE
+        customRingtone.setText(R.string.ringtone_custom_prompt)
         stopCustomPreview()
     }
 
@@ -252,10 +250,10 @@ class RingtoneActivity : AppCompatActivity() {
             displayFileTooBigDialog()
         } else {
             // resetState will stop the preview
-            adapter!!.resetState()
-            customRingtone!!.text = ringtone.name
-            customSelected!!.visibility = View.VISIBLE
-            customPlaying!!.visibility = View.VISIBLE
+            adapter.resetState()
+            customRingtone.text = ringtone.name
+            customSelected.visibility = View.VISIBLE
+            customPlaying.visibility = View.VISIBLE
             Glide.with(this)
                 .load(R.raw.baseline_graphic_eq_black_24dp)
                 .placeholder(R.drawable.baseline_graphic_eq_24)
@@ -317,14 +315,8 @@ class RingtoneActivity : AppCompatActivity() {
                 cr.takePersistableUriPermission(uri, takeFlags)
                 AndroidFileUtils.getCacheFile(this, uri)
                     .observeOn(AndroidSchedulers.mainThread())
-                    .subscribe(
-                        { ringtone: File -> onFileFound(ringtone) }
-                    ) {
-                        Toast.makeText(
-                            this,
-                            "Can't load ringtone !",
-                            Toast.LENGTH_SHORT
-                        ).show()
+                    .subscribe({ ringtone: File -> onFileFound(ringtone) }) {
+                        Toast.makeText(this, "Can't load ringtone !", Toast.LENGTH_SHORT).show()
                     }
             }
         }
@@ -342,13 +334,12 @@ class RingtoneActivity : AppCompatActivity() {
          */
         fun stripFileNameExtension(fileName: String): String {
             val index = fileName.lastIndexOf('.')
-            return if (index == -1) {
+            return if (index == -1)
                 fileName
-            } else {
+            else
                 fileName.substring(0, index)
-            }
         }
 
-        private val TAG = RingtoneActivity::class.java.simpleName
+        private val TAG = RingtoneActivity::class.simpleName!!
     }
 }
\ No newline at end of file
diff --git a/ring-android/app/src/main/java/cx/ring/client/ShareActivity.kt b/ring-android/app/src/main/java/cx/ring/client/ShareActivity.kt
index f996fbf03..7e5649a97 100644
--- a/ring-android/app/src/main/java/cx/ring/client/ShareActivity.kt
+++ b/ring-android/app/src/main/java/cx/ring/client/ShareActivity.kt
@@ -22,6 +22,7 @@ import androidx.appcompat.app.AppCompatActivity
 import android.os.Bundle
 import cx.ring.utils.ConversationPath
 import cx.ring.R
+import cx.ring.application.JamiApplication
 import dagger.hilt.android.AndroidEntryPoint
 
 @AndroidEntryPoint
@@ -36,6 +37,7 @@ class ShareActivity : AppCompatActivity() {
             finish()
             return
         }
+        JamiApplication.instance?.startDaemon()
         setContentView(R.layout.activity_share)
     }
 }
\ No newline at end of file
diff --git a/ring-android/app/src/main/java/cx/ring/contactrequests/ContactRequestsFragment.kt b/ring-android/app/src/main/java/cx/ring/contactrequests/ContactRequestsFragment.kt
index 0d5373103..fe14ee55c 100644
--- a/ring-android/app/src/main/java/cx/ring/contactrequests/ContactRequestsFragment.kt
+++ b/ring-android/app/src/main/java/cx/ring/contactrequests/ContactRequestsFragment.kt
@@ -106,11 +106,11 @@ class ContactRequestsFragment :
         (requireActivity() as HomeActivity).startConversation(accountId, contactId)
     }
 
-    override fun onItemClick(smartListViewModel: SmartListViewModel) {
-        presenter.contactRequestClicked(smartListViewModel.accountId, smartListViewModel.uri)
+    override fun onItemClick(item: SmartListViewModel) {
+        presenter.contactRequestClicked(item.accountId, item.uri)
     }
 
-    override fun onItemLongClick(smartListViewModel: SmartListViewModel) {}
+    override fun onItemLongClick(item: SmartListViewModel) {}
 
     companion object {
         private val TAG = ContactRequestsFragment::class.java.simpleName
diff --git a/ring-android/app/src/main/java/cx/ring/fragments/AccountMigrationFragment.java b/ring-android/app/src/main/java/cx/ring/fragments/AccountMigrationFragment.java
index b5c369114..74e7a117a 100644
--- a/ring-android/app/src/main/java/cx/ring/fragments/AccountMigrationFragment.java
+++ b/ring-android/app/src/main/java/cx/ring/fragments/AccountMigrationFragment.java
@@ -43,6 +43,7 @@ import androidx.fragment.app.Fragment;
 import com.google.android.material.dialog.MaterialAlertDialogBuilder;
 
 import cx.ring.R;
+import cx.ring.account.AccountEditionFragment;
 import cx.ring.application.JamiApplication;
 import cx.ring.databinding.FragAccountMigrationBinding;
 import dagger.hilt.android.AndroidEntryPoint;
@@ -56,7 +57,6 @@ import net.jami.services.AccountService;
 
 @AndroidEntryPoint
 public class AccountMigrationFragment extends Fragment {
-    public static final String ACCOUNT_ID = "ACCOUNT_ID";
     static final String TAG = AccountMigrationFragment.class.getSimpleName();
     @Inject
     AccountService mAccountService;
@@ -99,7 +99,7 @@ public class AccountMigrationFragment extends Fragment {
     public void onResume() {
         super.onResume();
         if (getArguments() != null) {
-            mAccountId = getArguments().getString(ACCOUNT_ID);
+            mAccountId = getArguments().getString(AccountEditionFragment.ACCOUNT_ID_KEY);
         }
     }
 
diff --git a/ring-android/app/src/main/java/cx/ring/fragments/AdvancedAccountFragment.kt b/ring-android/app/src/main/java/cx/ring/fragments/AdvancedAccountFragment.kt
index 98c0cbab3..c3a5f0518 100644
--- a/ring-android/app/src/main/java/cx/ring/fragments/AdvancedAccountFragment.kt
+++ b/ring-android/app/src/main/java/cx/ring/fragments/AdvancedAccountFragment.kt
@@ -35,18 +35,20 @@ import cx.ring.views.PasswordPreference
 import dagger.hilt.android.AndroidEntryPoint
 import net.jami.model.AccountConfig
 import net.jami.model.ConfigKey
+import net.jami.settings.AdvancedAccountPresenter
+import net.jami.settings.AdvancedAccountView
 
 @AndroidEntryPoint
 class AdvancedAccountFragment : BasePreferenceFragment<AdvancedAccountPresenter>(),
     AdvancedAccountView, Preference.OnPreferenceChangeListener {
 
-    override fun onCreatePreferences(bundle: Bundle?, rootKey: String?) {
-        super.onCreatePreferences(bundle, rootKey)
+    override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
+        super.onCreatePreferences(savedInstanceState, rootKey)
 
         // Load the preferences from an XML resource
         addPreferencesFromResource(R.xml.account_advanced_prefs)
         val args = arguments
-        presenter!!.init(args?.getString(AccountEditionFragment.ACCOUNT_ID_KEY))
+        presenter.init(args?.getString(AccountEditionFragment.ACCOUNT_ID_KEY))
     }
 
     override fun onDisplayPreferenceDialog(preference: Preference) {
@@ -79,18 +81,18 @@ class AdvancedAccountFragment : BasePreferenceFragment<AdvancedAccountPresenter>
             if (pref != null) {
                 pref.onPreferenceChangeListener = this
                 if (confKey == ConfigKey.LOCAL_INTERFACE) {
-                    val `val` = config[confKey]
+                    val value = config[confKey]
                     val display = networkInterfaces.toTypedArray()
                     val listPref = pref as ListPreference
                     listPref.entries = display
                     listPref.entryValues = display
-                    listPref.summary = `val`
-                    listPref.value = `val`
+                    listPref.summary = value
+                    listPref.value = value
                 } else if (!confKey.isTwoState) {
-                    val `val` = config[confKey]
-                    pref.summary = `val`
+                    val value = config[confKey]
+                    pref.summary = value
                     if (pref is EditTextPreference) {
-                        pref.text = `val`
+                        pref.text = value
                     }
                 } else {
                     (pref as TwoStatePreference).isChecked = config.getBool(confKey)
@@ -117,18 +119,18 @@ class AdvancedAccountFragment : BasePreferenceFragment<AdvancedAccountPresenter>
     }
 
     override fun onPreferenceChange(preference: Preference, newValue: Any): Boolean {
-        val key = ConfigKey.fromString(preference.key)
-        presenter!!.preferenceChanged(key, newValue)
+        val key = ConfigKey.fromString(preference.key)!!
+        presenter.preferenceChanged(key, newValue)
         when (preference) {
             is TwoStatePreference -> {
-                presenter!!.twoStatePreferenceChanged(key, newValue)
+                presenter.twoStatePreferenceChanged(key, newValue)
             }
             is PasswordPreference -> {
-                presenter!!.passwordPreferenceChanged(key, newValue)
+                presenter.passwordPreferenceChanged(key, newValue)
                 preference.setSummary(if (TextUtils.isEmpty(newValue.toString())) "" else "******")
             }
             else -> {
-                presenter!!.preferenceChanged(key, newValue)
+                presenter.preferenceChanged(key, newValue)
                 preference.summary = newValue.toString()
             }
         }
@@ -136,8 +138,7 @@ class AdvancedAccountFragment : BasePreferenceFragment<AdvancedAccountPresenter>
     }
 
     companion object {
-        @JvmField
-        val TAG = AdvancedAccountFragment::class.java.simpleName
+        val TAG = AdvancedAccountFragment::class.simpleName!!
         private const val DIALOG_FRAGMENT_TAG = "androidx.preference.PreferenceFragment.DIALOG"
     }
 }
\ No newline at end of file
diff --git a/ring-android/app/src/main/java/cx/ring/fragments/AdvancedAccountPresenter.java b/ring-android/app/src/main/java/cx/ring/fragments/AdvancedAccountPresenter.java
deleted file mode 100644
index 4d2569e4a..000000000
--- a/ring-android/app/src/main/java/cx/ring/fragments/AdvancedAccountPresenter.java
+++ /dev/null
@@ -1,110 +0,0 @@
-/*
- *  Copyright (C) 2004-2021 Savoir-faire Linux Inc.
- *
- *  Author: Hadrien De Sousa <hadrien.desousa@savoirfairelinux.com>
- *
- *  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, write to the Free Software
- *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-package cx.ring.fragments;
-
-import android.util.Log;
-
-import java.net.NetworkInterface;
-import java.net.SocketException;
-import java.util.ArrayList;
-import java.util.Enumeration;
-
-import javax.inject.Inject;
-
-import net.jami.services.ConversationFacade;
-import net.jami.model.Account;
-import net.jami.model.ConfigKey;
-import net.jami.mvp.RootPresenter;
-import net.jami.services.AccountService;
-
-public class AdvancedAccountPresenter extends RootPresenter<AdvancedAccountView> {
-
-    public static final String TAG = AdvancedAccountPresenter.class.getSimpleName();
-
-    protected ConversationFacade mConversationFacade;
-    protected AccountService mAccountService;
-
-    private Account mAccount;
-
-    @Inject
-    public AdvancedAccountPresenter(ConversationFacade conversationFacade, AccountService accountService) {
-        this.mConversationFacade = conversationFacade;
-        this.mAccountService = accountService;
-    }
-
-    public void init(String accountId) {
-        mAccount = mAccountService.getAccount(accountId);
-        if (mAccount != null) {
-            getView().initView(mAccount.getConfig(), getNetworkInterfaces());
-        }
-    }
-
-    public void twoStatePreferenceChanged(ConfigKey configKey, Object newValue) {
-        mAccount.setDetail(configKey, newValue.toString());
-        updateAccount();
-    }
-
-    public void passwordPreferenceChanged(ConfigKey configKey, Object newValue) {
-        mAccount.setDetail(configKey, newValue.toString());
-        updateAccount();
-    }
-
-    public void preferenceChanged(ConfigKey configKey, Object newValue) {
-        if (configKey == ConfigKey.AUDIO_PORT_MAX || configKey == ConfigKey.AUDIO_PORT_MIN) {
-            newValue = adjustRtpRange(Integer.valueOf((String) newValue));
-        }
-        mAccount.setDetail(configKey, newValue.toString());
-        updateAccount();
-    }
-
-    private void updateAccount() {
-        mAccountService.setCredentials(mAccount.getAccountID(), mAccount.getCredentialsHashMapList());
-        mAccountService.setAccountDetails(mAccount.getAccountID(), mAccount.getDetails());
-    }
-
-    private String adjustRtpRange(int newValue) {
-        if (newValue < 1024) {
-            return "1024";
-        }
-
-        if (newValue > 65534) {
-            return "65534";
-        }
-
-        return String.valueOf(newValue);
-    }
-
-    private ArrayList<CharSequence> getNetworkInterfaces() {
-        ArrayList<CharSequence> result = new ArrayList<>();
-        result.add("default");
-        try {
-
-            for (Enumeration<NetworkInterface> list = NetworkInterface.getNetworkInterfaces(); list.hasMoreElements(); ) {
-                NetworkInterface i = list.nextElement();
-                if (i.isUp()) {
-                    result.add(i.getDisplayName());
-                }
-            }
-        } catch (SocketException e) {
-            Log.e(TAG, "Error enumerating interfaces: ", e);
-        }
-        return result;
-    }
-}
diff --git a/ring-android/app/src/main/java/cx/ring/fragments/CallFragment.kt b/ring-android/app/src/main/java/cx/ring/fragments/CallFragment.kt
index 0ffe99af7..9b3b0587e 100644
--- a/ring-android/app/src/main/java/cx/ring/fragments/CallFragment.kt
+++ b/ring-android/app/src/main/java/cx/ring/fragments/CallFragment.kt
@@ -770,17 +770,16 @@ class CallFragment : BaseSupportFragment<CallPresenter, CallView>(), CallView,
         var display = display
         Log.w(TAG, "displayHangupButton $display")
         display = display and !isChoosePluginMode
-        binding!!.callControlGroup.visibility = if (display) View.VISIBLE else View.GONE
-        binding!!.callHangupBtn.visibility =
-            if (display) View.VISIBLE else View.GONE
-        binding!!.confControlGroup.visibility =
-            if (mConferenceMode && display) View.VISIBLE else View.GONE
+        binding?.apply {
+            callControlGroup.visibility = if (display) View.VISIBLE else View.GONE
+            callHangupBtn.visibility = if (display) View.VISIBLE else View.GONE
+            confControlGroup.visibility = if (mConferenceMode && display) View.VISIBLE else View.GONE
+        }
     }
 
     override fun displayDialPadKeyboard() {
         binding!!.dialpadEditText.requestFocus()
-        val imm =
-            binding!!.dialpadEditText.context.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
+        val imm = binding!!.dialpadEditText.context.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
         imm.toggleSoftInput(InputMethodManager.SHOW_FORCED, InputMethodManager.HIDE_IMPLICIT_ONLY)
     }
 
@@ -789,8 +788,7 @@ class CallFragment : BaseSupportFragment<CallPresenter, CallView>(), CallView,
     }
 
     override fun updateAudioState(state: AudioState) {
-        binding!!.callSpeakerBtn.isChecked =
-            state.outputType == HardwareService.AudioOutput.SPEAKERS
+        binding!!.callSpeakerBtn.isChecked = state.outputType == HardwareService.AudioOutput.SPEAKERS
     }
 
     override fun updateMenu() {
@@ -799,8 +797,7 @@ class CallFragment : BaseSupportFragment<CallPresenter, CallView>(), CallView,
 
     override fun updateTime(duration: Long) {
         binding?.let { binding ->
-            if (duration <= 0) binding.callStatusTxt.text =
-                null else binding.callStatusTxt.text = String.format(
+            binding.callStatusTxt.text = if (duration <= 0) null else String.format(
                 Locale.getDefault(),
                 "%d:%02d:%02d",
                 duration / 3600,
@@ -1189,7 +1186,7 @@ class CallFragment : BaseSupportFragment<CallPresenter, CallView>(), CallView,
         presenter.speakerClick(binding!!.callSpeakerBtn.isChecked)
     }
 
-    private fun startScreenShare(mediaProjection: MediaProjection) {
+    private fun startScreenShare(mediaProjection: MediaProjection?) {
         if (presenter.startScreenShare(mediaProjection)) {
             if (isChoosePluginMode) {
                 binding!!.pluginPreviewSurface.visibility = View.GONE
@@ -1324,8 +1321,8 @@ class CallFragment : BaseSupportFragment<CallPresenter, CallView>(), CallView,
         if (pluginsModeFirst) {
             // Init
             val callMediaHandlers = JamiService.getCallMediaHandlers()
-            val videoPluginsItems: MutableList<Drawable?> = ArrayList(callMediaHandlers.size + 1)
-            videoPluginsItems.add(context.getDrawable(R.drawable.baseline_cancel_24))
+            val videoPluginsItems: MutableList<Drawable> = ArrayList(callMediaHandlers.size + 1)
+            videoPluginsItems.add(context.getDrawable(R.drawable.baseline_cancel_24)!!)
             // Search for plugin call media handlers icons
             // If a call media handler doesn't have an icon use a standard android icon
             for (callMediaHandler in callMediaHandlers) {
@@ -1333,10 +1330,7 @@ class CallFragment : BaseSupportFragment<CallPresenter, CallView>(), CallView,
                 var drawablePath = details["iconPath"]
                 if (drawablePath != null && drawablePath.endsWith("svg")) drawablePath =
                     drawablePath.replace(".svg", ".png")
-                var handlerIcon = Drawable.createFromPath(drawablePath)
-                if (handlerIcon == null) {
-                    handlerIcon = context.getDrawable(R.drawable.ic_jami)
-                }
+                val handlerIcon = Drawable.createFromPath(drawablePath) ?: context.getDrawable(R.drawable.ic_jami)!!
                 videoPluginsItems.add(handlerIcon)
             }
             rp!!.updateData(videoPluginsItems)
diff --git a/ring-android/app/src/main/java/cx/ring/fragments/CodecPreference.java b/ring-android/app/src/main/java/cx/ring/fragments/CodecPreference.java
deleted file mode 100644
index 9a87bf776..000000000
--- a/ring-android/app/src/main/java/cx/ring/fragments/CodecPreference.java
+++ /dev/null
@@ -1,189 +0,0 @@
-/*
- *  Copyright (C) 2004-2021 Savoir-faire Linux Inc.
- *
- *  Author: Adrien Béraud <adrien.beraud@savoirfairelinux.com>
- *  Author: Alexandre Lision <alexandre.lision@savoirfairelinux.com>
- *
- *  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, write to the Free Software
- *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-package cx.ring.fragments;
-
-import android.content.Context;
-import androidx.preference.Preference;
-import androidx.preference.PreferenceViewHolder;
-import androidx.recyclerview.widget.RecyclerView;
-import android.util.AttributeSet;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.BaseAdapter;
-import android.widget.CheckBox;
-import android.widget.LinearLayout;
-import android.widget.ListAdapter;
-import android.widget.ListView;
-import android.widget.TextView;
-
-import java.util.ArrayList;
-
-import cx.ring.R;
-import net.jami.model.Codec;
-
-class CodecPreference extends Preference {
-    private static final String TAG = CodecPreference.class.getSimpleName();
-
-    private final CodecAdapter listAdapter;
-
-    public CodecPreference(Context context, AttributeSet attrs) {
-        super(context, attrs);
-        setWidgetLayoutResource(R.layout.frag_audio_mgmt);
-        listAdapter = new CodecAdapter(getContext());
-
-    }
-
-    public CodecPreference(Context context, AttributeSet attrs, int defStyleAttr) {
-        super(context, attrs, defStyleAttr);
-        setWidgetLayoutResource(R.layout.frag_audio_mgmt);
-        listAdapter = new CodecAdapter(getContext());
-    }
-
-    private void setListViewHeight(ListView listView, LinearLayout llMain) {
-        ListAdapter listAdapter = listView.getAdapter();
-        if (listAdapter == null) {
-            return;
-        }
-
-        int totalHeight = 0;
-        int firstHeight;
-        int desiredWidth = View.MeasureSpec.makeMeasureSpec(listView.getWidth(), View.MeasureSpec.AT_MOST);
-
-        for (int i = 0; i < listAdapter.getCount(); i++) {
-            View listItem = listAdapter.getView(i, null, listView);
-            listItem.measure(desiredWidth, View.MeasureSpec.UNSPECIFIED);
-            firstHeight = listItem.getMeasuredHeight();
-            totalHeight += firstHeight;
-        }
-
-        RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) llMain.getLayoutParams();
-        params.height = totalHeight + (listView.getDividerHeight() * (listAdapter.getCount()));
-        llMain.setLayoutParams(params);
-    }
-
-    @Override
-    public void onBindViewHolder(PreferenceViewHolder holder) {
-        super.onBindViewHolder(holder);
-
-        ListView mCodecList = (ListView) holder.findViewById(R.id.dndlistview);
-        mCodecList.setFocusable(false);
-        if (mCodecList.getAdapter() != listAdapter)
-            mCodecList.setAdapter(listAdapter);
-        mCodecList.setOnItemClickListener((arg0, arg1, pos, arg3) -> {
-            listAdapter.getItem(pos).toggleState();
-            listAdapter.notifyDataSetChanged();
-            callChangeListener(getActiveCodecList());
-        });
-
-        setListViewHeight(mCodecList, (LinearLayout) mCodecList.getParent());
-    }
-
-    ArrayList<Long> getActiveCodecList() {
-        ArrayList<Long> results = new ArrayList<>();
-        for (int i = 0; i < listAdapter.getCount(); ++i) {
-            if (listAdapter.getItem(i).isEnabled()) {
-                results.add(listAdapter.getItem(i).getPayload());
-            }
-        }
-        return results;
-    }
-
-    void setCodecs(ArrayList<Codec> codecs) {
-        listAdapter.setDataset(codecs);
-        notifyChanged();
-        notifyHierarchyChanged();
-    }
-
-    void refresh() {
-        if (null != this.listAdapter) {
-            this.listAdapter.notifyDataSetChanged();
-        }
-    }
-
-    private static class CodecAdapter extends BaseAdapter {
-        private final ArrayList<Codec> items = new ArrayList<>();
-        private Context mContext;
-
-        CodecAdapter(Context context) {
-            mContext = context;
-        }
-
-        @Override
-        public int getCount() {
-            return items.size();
-        }
-
-        @Override
-        public Codec getItem(int position) {
-            return items.get(position);
-        }
-
-        @Override
-        public long getItemId(int position) {
-            return 0;
-        }
-
-        @Override
-        public View getView(int pos, View convertView, ViewGroup parent) {
-            View rowView = convertView;
-            CodecView entryView;
-
-            if (rowView == null) {
-                LayoutInflater inflater = LayoutInflater.from(mContext);
-                rowView = inflater.inflate(R.layout.item_codec, parent, false);
-
-                entryView = new CodecView();
-                entryView.name = rowView.findViewById(R.id.codec_name);
-                entryView.samplerate = rowView.findViewById(R.id.codec_samplerate);
-                entryView.enabled = rowView.findViewById(R.id.codec_checked);
-                rowView.setTag(entryView);
-            } else {
-                entryView = (CodecView) rowView.getTag();
-            }
-
-            Codec codec = items.get(pos);
-
-            if (codec.isSpeex())
-                entryView.samplerate.setVisibility(View.VISIBLE);
-            else
-                entryView.samplerate.setVisibility(View.GONE);
-
-            entryView.name.setText(codec.getName());
-            entryView.samplerate.setText(codec.getSampleRate());
-            entryView.enabled.setChecked(codec.isEnabled());
-
-            return rowView;
-        }
-
-        void setDataset(ArrayList<Codec> codecs) {
-            items.clear();
-            items.addAll(codecs);
-            notifyDataSetChanged();
-        }
-
-        class CodecView {
-            public TextView name;
-            public TextView samplerate;
-            public CheckBox enabled;
-        }
-    }
-}
diff --git a/ring-android/app/src/main/java/cx/ring/fragments/CodecPreference.kt b/ring-android/app/src/main/java/cx/ring/fragments/CodecPreference.kt
new file mode 100644
index 000000000..229c98383
--- /dev/null
+++ b/ring-android/app/src/main/java/cx/ring/fragments/CodecPreference.kt
@@ -0,0 +1,142 @@
+/*
+ *  Copyright (C) 2004-2021 Savoir-faire Linux Inc.
+ *
+ *  Author: Adrien Béraud <adrien.beraud@savoirfairelinux.com>
+ *  Author: Alexandre Lision <alexandre.lision@savoirfairelinux.com>
+ *
+ *  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, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+package cx.ring.fragments
+
+import android.content.Context
+import android.util.AttributeSet
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import android.widget.*
+import androidx.preference.Preference
+import androidx.preference.PreferenceViewHolder
+import androidx.recyclerview.widget.RecyclerView
+import cx.ring.R
+import net.jami.model.Codec
+
+internal class CodecPreference @JvmOverloads constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int = 0) :
+    Preference(context, attrs, defStyleAttr) {
+    private val listAdapter: CodecAdapter = CodecAdapter(context)
+
+    private fun setListViewHeight(listView: ListView, llMain: LinearLayout) {
+        val listAdapter = listView.adapter ?: return
+        var totalHeight = 0
+        var firstHeight: Int
+        val desiredWidth = View.MeasureSpec.makeMeasureSpec(listView.width, View.MeasureSpec.AT_MOST)
+        for (i in 0 until listAdapter.count) {
+            val listItem = listAdapter.getView(i, null, listView)
+            listItem.measure(desiredWidth, View.MeasureSpec.UNSPECIFIED)
+            firstHeight = listItem.measuredHeight
+            totalHeight += firstHeight
+        }
+        val params = llMain.layoutParams as RecyclerView.LayoutParams
+        params.height = totalHeight + listView.dividerHeight * listAdapter.count
+        llMain.layoutParams = params
+    }
+
+    override fun onBindViewHolder(holder: PreferenceViewHolder) {
+        super.onBindViewHolder(holder)
+        val mCodecList = holder.findViewById(R.id.dndlistview) as ListView
+        mCodecList.isFocusable = false
+        if (mCodecList.adapter !== listAdapter)
+            mCodecList.adapter = listAdapter
+        mCodecList.onItemClickListener = AdapterView.OnItemClickListener { _, _, pos: Int, _ ->
+            listAdapter.getItem(pos).toggleState()
+            listAdapter.notifyDataSetChanged()
+            callChangeListener(activeCodecList)
+        }
+        setListViewHeight(mCodecList, mCodecList.parent as LinearLayout)
+    }
+
+    val activeCodecList: ArrayList<Long>
+        get() {
+            val results = ArrayList<Long>()
+            for (i in 0 until listAdapter.count) {
+                if (listAdapter.getItem(i).isEnabled) {
+                    results.add(listAdapter.getItem(i).payload)
+                }
+            }
+            return results
+        }
+
+    fun setCodecs(codecs: ArrayList<Codec>) {
+        listAdapter.setDataset(codecs)
+        notifyChanged()
+        notifyHierarchyChanged()
+    }
+
+    fun refresh() {
+        listAdapter.notifyDataSetChanged()
+    }
+
+    private class CodecAdapter constructor(private val mContext: Context) : BaseAdapter() {
+        private val items = ArrayList<Codec>()
+        override fun getCount(): Int {
+            return items.size
+        }
+
+        override fun getItem(position: Int): Codec {
+            return items[position]
+        }
+
+        override fun getItemId(position: Int): Long {
+            return 0
+        }
+
+        override fun getView(pos: Int, convertView: View?, parent: ViewGroup): View {
+            val entryView: CodecView
+            var rowView = convertView
+            if (rowView == null) {
+                rowView = LayoutInflater.from(mContext).inflate(R.layout.item_codec, parent, false)
+                entryView = CodecView(
+                    rowView.findViewById(R.id.codec_name),
+                    rowView.findViewById(R.id.codec_samplerate),
+                    rowView.findViewById(R.id.codec_checked)
+                )
+                rowView.tag = entryView
+            } else {
+                entryView = rowView.tag as CodecView
+            }
+            val codec = items[pos]
+            entryView.name.text = codec.name
+            entryView.samplerate.visibility = if (codec.isSpeex) View.VISIBLE else View.GONE
+            entryView.samplerate.text = codec.sampleRate
+            entryView.enabled.isChecked = codec.isEnabled
+            return rowView!!
+        }
+
+        fun setDataset(codecs: ArrayList<Codec>) {
+            items.clear()
+            items.addAll(codecs)
+            notifyDataSetChanged()
+        }
+
+        class CodecView (
+            val name: TextView,
+            val samplerate: TextView,
+            val enabled: CheckBox
+        )
+    }
+
+    init {
+        widgetLayoutResource = R.layout.frag_audio_mgmt
+    }
+}
\ No newline at end of file
diff --git a/ring-android/app/src/main/java/cx/ring/fragments/ContactPickerFragment.java b/ring-android/app/src/main/java/cx/ring/fragments/ContactPickerFragment.java
deleted file mode 100644
index d60da326e..000000000
--- a/ring-android/app/src/main/java/cx/ring/fragments/ContactPickerFragment.java
+++ /dev/null
@@ -1,170 +0,0 @@
-package cx.ring.fragments;
-
-import android.app.Dialog;
-import android.os.Bundle;
-
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.FrameLayout;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-
-import com.google.android.material.bottomsheet.BottomSheetBehavior;
-import com.google.android.material.bottomsheet.BottomSheetDialog;
-import com.google.android.material.bottomsheet.BottomSheetDialogFragment;
-import com.google.android.material.chip.Chip;
-
-import java.util.HashSet;
-import java.util.Set;
-
-import javax.inject.Inject;
-
-import cx.ring.R;
-import cx.ring.adapters.SmartListAdapter;
-import cx.ring.client.HomeActivity;
-import cx.ring.databinding.FragContactPickerBinding;
-import net.jami.services.ConversationFacade;
-import net.jami.model.Contact;
-import net.jami.smartlist.SmartListViewModel;
-import cx.ring.viewholders.SmartListViewHolder;
-import cx.ring.views.AvatarDrawable;
-import dagger.hilt.android.AndroidEntryPoint;
-import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
-import io.reactivex.rxjava3.disposables.CompositeDisposable;
-
-@AndroidEntryPoint
-public class ContactPickerFragment extends BottomSheetDialogFragment {
-
-    public static final String TAG = ContactPickerFragment.class.getSimpleName();
-
-    private FragContactPickerBinding binding = null;
-    private SmartListAdapter adapter;
-    private final CompositeDisposable mDisposableBag = new CompositeDisposable();
-
-    private String mAccountId =  null;
-    private final Set<Contact> mCurrentSelection = new HashSet<>();
-
-    @Inject
-    ConversationFacade mConversationFacade;
-
-    public ContactPickerFragment() {
-        // Required empty public constructor
-    }
-
-    public static ContactPickerFragment newInstance() {
-        return new ContactPickerFragment();
-    }
-
-    @Override
-    public void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-        //setRetainInstance(true);
-        //((JamiApplication) getActivity().getApplication()).getInjectionComponent().inject(this);
-    }
-
-    @Override
-    public void onSaveInstanceState(@NonNull Bundle outState) {
-        super.onSaveInstanceState(outState);
-
-    }
-
-    @NonNull
-    @Override
-    public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) {
-        Dialog bdialog = super.onCreateDialog(savedInstanceState);
-        if (bdialog instanceof BottomSheetDialog) {
-            BottomSheetDialog dialog = (BottomSheetDialog) bdialog;
-            BottomSheetBehavior<FrameLayout> behavior = dialog.getBehavior();
-            behavior.setFitToContents(false);
-            behavior.setSkipCollapsed(true);
-            behavior.setState(BottomSheetBehavior.STATE_HALF_EXPANDED);
-        }
-        return bdialog;
-    }
-
-    @Override
-    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
-        mDisposableBag.add(mConversationFacade.getContactList()
-                .observeOn(AndroidSchedulers.mainThread())
-                .subscribe(conversations -> {
-                    if (binding == null)
-                        return;
-                    adapter.update(conversations);
-                }));
-    }
-
-    @Override
-    public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
-        binding = FragContactPickerBinding.inflate(getLayoutInflater(), container, false);
-        adapter = new SmartListAdapter(null, new SmartListViewHolder.SmartListListeners() {
-            @Override
-            public void onItemClick(SmartListViewModel item) {
-                mAccountId = item.getAccountId();
-
-                boolean checked = !item.isChecked();
-                item.setChecked(checked);
-                adapter.update(item);
-
-                Runnable remover = () -> {
-                    mCurrentSelection.remove(item.getContacts().get(0));
-                    if (mCurrentSelection.isEmpty())
-                        binding.createGroupBtn.setEnabled(false);
-                    item.setChecked(false);
-                    adapter.update(item);
-                    View v = binding.selectedContacts.findViewWithTag(item);
-                    if (v != null)
-                        binding.selectedContacts.removeView(v);
-                };
-
-                if (checked) {
-                    if (mCurrentSelection.add(item.getContacts().get(0))) {
-                        Chip chip = new Chip(requireContext(), null, R.style.Widget_MaterialComponents_Chip_Entry);
-                        chip.setText(item.getContactName());
-                        chip.setChipIcon(new AvatarDrawable.Builder()
-                                .withViewModel(item)
-                                .withCircleCrop(true)
-                                .withCheck(false)
-                                .build(binding.getRoot().getContext()));
-                        chip.setCloseIconVisible(true);
-                        chip.setTag(item);
-                        chip.setOnCloseIconClickListener(v -> remover.run());
-                        binding.selectedContacts.addView(chip);
-                    }
-                    binding.createGroupBtn.setEnabled(true);
-                } else {
-                    remover.run();
-                }
-            }
-
-            @Override
-            public void onItemLongClick(SmartListViewModel item) {
-
-            }
-        }, mDisposableBag);
-        binding.createGroupBtn.setOnClickListener(v -> mDisposableBag.add(mConversationFacade.createConversation(mAccountId, mCurrentSelection)
-                .observeOn(AndroidSchedulers.mainThread())
-                .subscribe(conversation -> {
-                    ((HomeActivity) requireActivity()).startConversation(mAccountId, conversation.getUri());
-                    Dialog dialog = getDialog();
-                    if (dialog != null)
-                        dialog.cancel();
-                })));
-        binding.contactList.setAdapter(adapter);
-        return binding.getRoot();
-    }
-
-    @Override
-    public void onDestroyView() {
-        mDisposableBag.clear();
-        binding = null;
-        adapter = null;
-        super.onDestroyView();
-    }
-
-    @Override
-    public int getTheme() {
-        return R.style.BottomSheetDialogTheme;
-    }
-}
\ No newline at end of file
diff --git a/ring-android/app/src/main/java/cx/ring/fragments/ContactPickerFragment.kt b/ring-android/app/src/main/java/cx/ring/fragments/ContactPickerFragment.kt
new file mode 100644
index 000000000..d458b0de1
--- /dev/null
+++ b/ring-android/app/src/main/java/cx/ring/fragments/ContactPickerFragment.kt
@@ -0,0 +1,138 @@
+package cx.ring.fragments
+
+import android.app.Dialog
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import com.google.android.material.bottomsheet.BottomSheetBehavior
+import com.google.android.material.bottomsheet.BottomSheetDialog
+import com.google.android.material.bottomsheet.BottomSheetDialogFragment
+import com.google.android.material.chip.Chip
+import cx.ring.R
+import cx.ring.adapters.SmartListAdapter
+import cx.ring.client.HomeActivity
+import cx.ring.databinding.FragContactPickerBinding
+import cx.ring.viewholders.SmartListViewHolder.SmartListListeners
+import cx.ring.views.AvatarDrawable
+import dagger.hilt.android.AndroidEntryPoint
+import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
+import io.reactivex.rxjava3.disposables.CompositeDisposable
+import net.jami.model.Contact
+import net.jami.model.Conversation
+import net.jami.services.ConversationFacade
+import net.jami.smartlist.SmartListViewModel
+import java.util.*
+import javax.inject.Inject
+
+@AndroidEntryPoint
+class ContactPickerFragment : BottomSheetDialogFragment() {
+    private var binding: FragContactPickerBinding? = null
+    private var adapter: SmartListAdapter? = null
+    private val mDisposableBag = CompositeDisposable()
+    private var mAccountId: String? = null
+    private val mCurrentSelection: MutableSet<Contact> = HashSet()
+
+    @JvmField
+    @Inject
+    var mConversationFacade: ConversationFacade? = null
+    override fun onCreate(savedInstanceState: Bundle?) {
+        super.onCreate(savedInstanceState)
+        //setRetainInstance(true);
+        //((JamiApplication) getActivity().getApplication()).getInjectionComponent().inject(this);
+    }
+
+    override fun onSaveInstanceState(outState: Bundle) {
+        super.onSaveInstanceState(outState)
+    }
+
+    override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
+        val bdialog = super.onCreateDialog(savedInstanceState)
+        if (bdialog is BottomSheetDialog) {
+            val behavior = bdialog.behavior
+            behavior.isFitToContents = false
+            behavior.skipCollapsed = true
+            behavior.state = BottomSheetBehavior.STATE_HALF_EXPANDED
+        }
+        return bdialog
+    }
+
+    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+        mDisposableBag.add(mConversationFacade!!.contactList
+            .observeOn(AndroidSchedulers.mainThread())
+            .subscribe { conversations: MutableList<SmartListViewModel> ->
+                if (binding == null) return@subscribe
+                adapter?.update(conversations)
+            })
+    }
+
+    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
+        binding = FragContactPickerBinding.inflate(layoutInflater, container, false)
+        adapter = SmartListAdapter(null, object : SmartListListeners {
+            override fun onItemClick(item: SmartListViewModel) {
+                mAccountId = item.accountId
+                val checked = !item.isChecked
+                item.isChecked = checked
+                adapter!!.update(item)
+                val remover = Runnable {
+                    mCurrentSelection.remove(item.contacts[0])
+                    if (mCurrentSelection.isEmpty()) binding!!.createGroupBtn.isEnabled = false
+                    item.isChecked = false
+                    adapter!!.update(item)
+                    val v = binding!!.selectedContacts.findViewWithTag<View>(item)
+                    if (v != null) binding!!.selectedContacts.removeView(v)
+                }
+                if (checked) {
+                    if (mCurrentSelection.add(item.contacts[0])) {
+                        val chip = Chip(requireContext(), null, R.style.Widget_MaterialComponents_Chip_Entry)
+                        chip.text = item.contactName
+                        chip.chipIcon = AvatarDrawable.Builder()
+                            .withViewModel(item)
+                            .withCircleCrop(true)
+                            .withCheck(false)
+                            .build(binding!!.root.context)
+                        chip.isCloseIconVisible = true
+                        chip.tag = item
+                        chip.setOnCloseIconClickListener { v: View? -> remover.run() }
+                        binding!!.selectedContacts.addView(chip)
+                    }
+                    binding!!.createGroupBtn.isEnabled = true
+                } else {
+                    remover.run()
+                }
+            }
+
+            override fun onItemLongClick(item: SmartListViewModel) {}
+        }, mDisposableBag)
+        binding!!.createGroupBtn.setOnClickListener { v: View? ->
+            mDisposableBag.add(
+                mConversationFacade!!.createConversation(mAccountId!!, mCurrentSelection)
+                    .observeOn(AndroidSchedulers.mainThread())
+                    .subscribe { conversation: Conversation ->
+                        (requireActivity() as HomeActivity).startConversation(conversation.accountId, conversation.uri)
+                        val dialog = dialog
+                        dialog?.cancel()
+                    })
+        }
+        binding!!.contactList.adapter = adapter
+        return binding!!.root
+    }
+
+    override fun onDestroyView() {
+        mDisposableBag.clear()
+        binding = null
+        adapter = null
+        super.onDestroyView()
+    }
+
+    override fun getTheme(): Int {
+        return R.style.BottomSheetDialogTheme
+    }
+
+    companion object {
+        val TAG = ContactPickerFragment::class.java.simpleName
+        fun newInstance(): ContactPickerFragment {
+            return ContactPickerFragment()
+        }
+    }
+}
\ No newline at end of file
diff --git a/ring-android/app/src/main/java/cx/ring/fragments/ConversationFragment.kt b/ring-android/app/src/main/java/cx/ring/fragments/ConversationFragment.kt
index aa5f54173..792b9e47d 100644
--- a/ring-android/app/src/main/java/cx/ring/fragments/ConversationFragment.kt
+++ b/ring-android/app/src/main/java/cx/ring/fragments/ConversationFragment.kt
@@ -65,8 +65,8 @@ import cx.ring.interfaces.Colorable
 import cx.ring.mvp.BaseSupportFragment
 import cx.ring.service.DRingService
 import cx.ring.service.LocationSharingService
-import cx.ring.service.LocationSharingService.LocalBinder
 import cx.ring.services.NotificationServiceImpl
+import cx.ring.services.SharedPreferencesServiceImpl
 import cx.ring.services.SharedPreferencesServiceImpl.Companion.getConversationPreferences
 import cx.ring.utils.ActionHelper
 import cx.ring.utils.AndroidFileUtils.copyFileToUri
@@ -77,7 +77,6 @@ import cx.ring.utils.AndroidFileUtils.getCacheFile
 import cx.ring.utils.AndroidFileUtils.getMimeTypeFromExtension
 import cx.ring.utils.AndroidFileUtils.getSpaceLeft
 import cx.ring.utils.ContentUriHandler
-import cx.ring.utils.ContentUriHandler.getUriForFile
 import cx.ring.utils.ConversationPath
 import cx.ring.utils.DeviceUtils.isTablet
 import cx.ring.utils.MediaButtonsHelper.MediaButtonsHelperCallback
@@ -156,6 +155,17 @@ class ConversationFragment : BaseSupportFragment<ConversationPresenter, Conversa
         }
     }
 
+    override fun displayErrorToast(error: Error) {
+        val errorString: String = when (error) {
+            Error.NO_INPUT -> getString(R.string.call_error_no_camera_no_microphone)
+            Error.INVALID_FILE -> getString(R.string.invalid_file)
+            Error.NOT_ABLE_TO_WRITE_FILE -> getString(R.string.not_able_to_write_file)
+            Error.NO_SPACE_LEFT -> getString(R.string.no_space_left_on_device)
+            else -> getString(R.string.generic_error)
+        }
+        Toast.makeText(requireContext(), errorString, Toast.LENGTH_LONG).show()
+    }
+
     override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
         val res = resources
         marginPx = res.getDimensionPixelSize(R.dimen.conversation_message_input_margin)
@@ -188,8 +198,7 @@ class ConversationFragment : BaseSupportFragment<ConversationPresenter, Conversa
             }
             binding.ongoingcallPane.visibility = View.GONE
             binding.msgInputTxt.setMediaListener { contentInfo: InputContentInfoCompat ->
-                startFileSend(
-                    getCacheFile(requireContext(), contentInfo.contentUri)
+                startFileSend(getCacheFile(requireContext(), contentInfo.contentUri)
                         .flatMapCompletable { file: File -> sendFile(file) }
                         .doFinally { contentInfo.releasePermission() })
             }
@@ -469,7 +478,7 @@ class ConversationFragment : BaseSupportFragment<ConversationPresenter, Conversa
                     putExtra("android.intent.extras.CAMERA_FACING", Camera.CameraInfo.CAMERA_FACING_FRONT)
                     putExtra("android.intent.extras.LENS_FACING_FRONT", 1)
                     putExtra("android.intent.extra.USE_FRONT_CAMERA", true)
-                    putExtra(MediaStore.EXTRA_OUTPUT, getUriForFile(context, ContentUriHandler.AUTHORITY_FILES, createVideoFile(context).apply {
+                    putExtra(MediaStore.EXTRA_OUTPUT, ContentUriHandler.getUriForFile(context, ContentUriHandler.AUTHORITY_FILES, createVideoFile(context).apply {
                         mCurrentPhoto = this
                     }))
                 }
@@ -490,7 +499,7 @@ class ConversationFragment : BaseSupportFragment<ConversationPresenter, Conversa
         try {
             val photoFile = createImageFile(c)
             Log.i(TAG, "takePicture: trying to save to $photoFile")
-            val photoURI = getUriForFile(c, ContentUriHandler.AUTHORITY_FILES, photoFile)
+            val photoURI = ContentUriHandler.getUriForFile(c, ContentUriHandler.AUTHORITY_FILES, photoFile)
             val takePictureIntent =
                 Intent(MediaStore.ACTION_IMAGE_CAPTURE).putExtra(MediaStore.EXTRA_OUTPUT, photoURI)
                     .putExtra("android.intent.extras.CAMERA_FACING", 1)
@@ -580,11 +589,7 @@ class ConversationFragment : BaseSupportFragment<ConversationPresenter, Conversa
                 { Toast.makeText(context, R.string.generic_error, Toast.LENGTH_SHORT).show() })
     }
 
-    override fun onRequestPermissionsResult(
-        requestCode: Int,
-        permissions: Array<String>,
-        grantResults: IntArray
-    ) {
+    override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<String>, grantResults: IntArray) {
         var i = 0
         val n = permissions.size
         while (i < n) {
@@ -669,7 +674,7 @@ class ConversationFragment : BaseSupportFragment<ConversationPresenter, Conversa
         val c = context ?: return
         var fileUri: Uri? = null
         try {
-            fileUri = getUriForFile(c, ContentUriHandler.AUTHORITY_FILES, path, displayName)
+            fileUri = ContentUriHandler.getUriForFile(c, ContentUriHandler.AUTHORITY_FILES, path, displayName)
         } catch (e: IllegalArgumentException) {
             Log.e("File Selector", "The selected file can't be shared: " + path.name)
         }
@@ -689,7 +694,7 @@ class ConversationFragment : BaseSupportFragment<ConversationPresenter, Conversa
         val c = context ?: return
         var fileUri: Uri? = null
         try {
-            fileUri = getUriForFile(c, ContentUriHandler.AUTHORITY_FILES, path, displayName)
+            fileUri = ContentUriHandler.getUriForFile(c, ContentUriHandler.AUTHORITY_FILES, path, displayName)
         } catch (e: IllegalArgumentException) {
             Log.e(TAG, "The selected file can't be shared: " + path.name)
         }
@@ -805,7 +810,7 @@ class ConversationFragment : BaseSupportFragment<ConversationPresenter, Conversa
             connection = object : ServiceConnection {
                 override fun onServiceConnected(name: ComponentName, service: IBinder) {
                     Log.w(TAG, "onServiceConnected")
-                    val binder = service as LocalBinder
+                    val binder = service as LocationSharingService.LocalBinder
                     val locationService = binder.service
                     //val path = ConversationPath(presenter.path)
                     if (locationService.isSharing(path)) {
diff --git a/ring-android/app/src/main/java/cx/ring/fragments/GeneralAccountFragment.java b/ring-android/app/src/main/java/cx/ring/fragments/GeneralAccountFragment.java
deleted file mode 100644
index f414197c3..000000000
--- a/ring-android/app/src/main/java/cx/ring/fragments/GeneralAccountFragment.java
+++ /dev/null
@@ -1,265 +0,0 @@
-/*
- *  Copyright (C) 2004-2021 Savoir-faire Linux Inc.
- *
- *  Author: Alexandre Lision <alexandre.lision@savoirfairelinux.com>
- *          Adrien Béraud <adrien.beraud@savoirfairelinux.com>
- *
- *  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 cx.ring.fragments;
-
-import android.app.Activity;
-import android.content.Context;
-import android.os.Bundle;
-import android.text.format.Formatter;
-import android.view.inputmethod.EditorInfo;
-
-import androidx.annotation.NonNull;
-import androidx.appcompat.app.AlertDialog;
-import androidx.fragment.app.FragmentManager;
-import androidx.preference.EditTextPreference;
-import androidx.preference.Preference;
-import androidx.preference.PreferenceManager;
-import androidx.preference.SeekBarPreference;
-import androidx.preference.SwitchPreference;
-import androidx.preference.TwoStatePreference;
-
-import com.google.android.material.dialog.MaterialAlertDialogBuilder;
-
-import cx.ring.R;
-import cx.ring.account.AccountEditionFragment;
-import cx.ring.application.JamiApplication;
-import net.jami.model.Account;
-import net.jami.model.AccountConfig;
-import net.jami.model.ConfigKey;
-import cx.ring.mvp.BasePreferenceFragment;
-import cx.ring.services.SharedPreferencesServiceImpl;
-import net.jami.utils.Log;
-import net.jami.utils.Tuple;
-import cx.ring.views.EditTextIntegerPreference;
-import cx.ring.views.EditTextPreferenceDialog;
-import cx.ring.views.PasswordPreference;
-import dagger.hilt.android.AndroidEntryPoint;
-
-@AndroidEntryPoint
-public class GeneralAccountFragment extends BasePreferenceFragment<GeneralAccountPresenter> implements GeneralAccountView {
-
-    public static final String TAG = GeneralAccountFragment.class.getSimpleName();
-
-    private static final String DIALOG_FRAGMENT_TAG = "androidx.preference.PreferenceFragment.DIALOG";
-    private final Preference.OnPreferenceChangeListener changeAccountStatusListener = (preference, newValue) -> {
-        presenter.setEnabled((Boolean) newValue);
-        return false;
-    };
-    private final Preference.OnPreferenceChangeListener changeBasicPreferenceListener = (preference, newValue) -> {
-        Log.i(TAG, "Changing preference " + preference.getKey() + " to value:" + newValue);
-        final ConfigKey key = ConfigKey.fromString(preference.getKey());
-        if (preference instanceof TwoStatePreference) {
-            presenter.twoStatePreferenceChanged(key, newValue);
-        } else if (preference instanceof PasswordPreference) {
-            StringBuilder tmp = new StringBuilder();
-            for (int i = 0; i < ((String) newValue).length(); ++i) {
-                tmp.append("*");
-            }
-            preference.setSummary(tmp.toString());
-            presenter.passwordPreferenceChanged(key, newValue);
-        } else if (key == ConfigKey.ACCOUNT_USERNAME) {
-            presenter.userNameChanged(key, newValue);
-            preference.setSummary((CharSequence) newValue);
-        } else {
-            preference.setSummary((CharSequence) newValue);
-            presenter.preferenceChanged(key, newValue);
-        }
-        return true;
-    };
-
-    public static GeneralAccountFragment newInstance(@NonNull String accountId) {
-        Bundle bundle = new Bundle();
-        bundle.putString(AccountEditionFragment.ACCOUNT_ID_KEY, accountId);
-        GeneralAccountFragment generalAccountFragment = new GeneralAccountFragment();
-        generalAccountFragment.setArguments(bundle);
-        return generalAccountFragment;
-    }
-
-    @Override
-    public void accountChanged(@NonNull Account account) {
-        PreferenceManager pm = getPreferenceManager();
-        pm.setSharedPreferencesMode(Context.MODE_PRIVATE);
-        pm.setSharedPreferencesName(SharedPreferencesServiceImpl.PREFS_ACCOUNT+account.getAccountID());
-
-        setPreferenceDetails(account.getConfig());
-
-        SwitchPreference pref = findPreference("Account.status");
-        if (account.isSip() && pref != null) {
-            String status;
-            pref.setTitle(account.getAlias());
-            if (account.isEnabled()) {
-                if (account.isTrying()) {
-                    status = getString(R.string.account_status_connecting);
-                } else if (account.needsMigration()) {
-                    status = getString(R.string.account_update_needed);
-                } else if (account.isInError()) {
-                    status = getString(R.string.account_status_connection_error);
-                } else if (account.isRegistered()) {
-                    status = getString(R.string.account_status_online);
-                } else {
-                    status = getString(R.string.account_status_unknown);
-                }
-            } else {
-                status = getString(R.string.account_status_offline);
-            }
-            pref.setSummary(status);
-            pref.setChecked(account.isEnabled());
-
-            // An ip2ip account is always ready
-            pref.setEnabled(!account.isIP2IP());
-
-            pref.setOnPreferenceChangeListener(changeAccountStatusListener);
-        }
-
-        setPreferenceListener(account.getConfig(), changeBasicPreferenceListener);
-    }
-
-    @Override
-    public void finish() {
-        Activity activity = getActivity();
-        if (activity != null)
-            activity.onBackPressed();
-    }
-
-    @Override
-    public void updateResolutions(Tuple<Integer, Integer> maxResolution, int currentResolution) {
-    }
-
-    private CharSequence getFileSizeSummary(int size, int maxSize) {
-        if (size == 0)  {
-            return getText(R.string.account_accept_files_never);
-        } else if (size == maxSize) {
-            return getText(R.string.account_accept_files_always);
-        } else {
-            return Formatter.formatFileSize(requireContext(), size * 1000 * 1000);
-        }
-    }
-
-    @Override
-    public void onCreatePreferences(Bundle bundle, String rootKey) {
-        super.onCreatePreferences(bundle, rootKey);
-
-        Bundle args = getArguments();
-        presenter.init(args == null  ? null : args.getString(AccountEditionFragment.ACCOUNT_ID_KEY));
-
-        SeekBarPreference filePref = findPreference("acceptIncomingFilesMaxSize");
-        if (filePref != null) {
-            filePref.setOnPreferenceChangeListener((p, v) ->  {
-                SeekBarPreference pref = (SeekBarPreference)p;
-                p.setSummary(getFileSizeSummary((Integer) v, pref.getMax()));
-                return true;
-            });
-            filePref.setSummary(getFileSizeSummary(filePref.getValue(), filePref.getMax()));
-        }
-
-        Preference deletePref = findPreference("Account.delete");
-        if (deletePref != null) {
-            deletePref.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
-                @Override
-                public boolean onPreferenceClick(Preference preference) {
-                    AlertDialog deleteDialog = createDeleteDialog();
-                    deleteDialog.show();
-                    return false;
-                }
-            });
-        }
-    }
-
-    @Override
-    public void onDisplayPreferenceDialog(Preference preference) {
-        FragmentManager fragmentManager = getParentFragmentManager();
-        if (fragmentManager.findFragmentByTag(DIALOG_FRAGMENT_TAG) != null) {
-            return;
-        }
-
-        if (preference instanceof EditTextIntegerPreference) {
-            EditTextPreferenceDialog f = EditTextPreferenceDialog.newInstance(preference.getKey(), EditorInfo.TYPE_CLASS_NUMBER);
-            f.setTargetFragment(this, 0);
-            f.show(fragmentManager, DIALOG_FRAGMENT_TAG);
-        } else if (preference instanceof PasswordPreference) {
-            EditTextPreferenceDialog f = EditTextPreferenceDialog.newInstance(preference.getKey(), EditorInfo.TYPE_CLASS_TEXT | EditorInfo.TYPE_TEXT_VARIATION_PASSWORD);
-            f.setTargetFragment(this, 0);
-            f.show(fragmentManager, DIALOG_FRAGMENT_TAG);
-        } else {
-            super.onDisplayPreferenceDialog(preference);
-        }
-    }
-
-    private void setPreferenceDetails(AccountConfig details) {
-        for (ConfigKey confKey : details.getKeys()) {
-            Preference pref = findPreference(confKey.key());
-            if (pref == null) {
-                continue;
-            }
-            if (!confKey.isTwoState()) {
-                String val = details.get(confKey);
-                ((EditTextPreference) pref).setText(val);
-                if (pref instanceof PasswordPreference) {
-                    StringBuilder tmp = new StringBuilder();
-                    for (int i = 0; i < val.length(); ++i) {
-                        tmp.append("*");
-                    }
-                    pref.setSummary(tmp.toString());
-                } else {
-                    pref.setSummary(val);
-                }
-            } else {
-                ((TwoStatePreference) pref).setChecked(details.getBool(confKey));
-            }
-        }
-    }
-
-    private void setPreferenceListener(AccountConfig details, Preference.OnPreferenceChangeListener listener) {
-        for (ConfigKey confKey : details.getKeys()) {
-            Preference pref = findPreference(confKey.key());
-            if (pref != null) {
-                pref.setOnPreferenceChangeListener(listener);
-            }
-        }
-    }
-
-    @Override
-    public void addJamiPreferences(String accountId) {
-        PreferenceManager pm = getPreferenceManager();
-        pm.setSharedPreferencesMode(Context.MODE_PRIVATE);
-        pm.setSharedPreferencesName(SharedPreferencesServiceImpl.PREFS_ACCOUNT+accountId);
-        addPreferencesFromResource(R.xml.account_prefs_jami);
-    }
-
-    @Override
-    public void addSipPreferences() {
-        addPreferencesFromResource(R.xml.account_general_prefs);
-    }
-
-    @NonNull
-    private AlertDialog createDeleteDialog() {
-        AlertDialog alertDialog = new MaterialAlertDialogBuilder(requireContext())
-                .setMessage(R.string.account_delete_dialog_message)
-                .setTitle(R.string.account_delete_dialog_title)
-                .setPositiveButton(R.string.menu_delete, (dialog, whichButton) -> presenter.removeAccount())
-                .setNegativeButton(android.R.string.cancel, null)
-                .create();
-        Activity activity = getActivity();
-        if (activity != null)
-            alertDialog.setOwnerActivity(getActivity());
-        return alertDialog;
-    }
-
-}
diff --git a/ring-android/app/src/main/java/cx/ring/fragments/GeneralAccountFragment.kt b/ring-android/app/src/main/java/cx/ring/fragments/GeneralAccountFragment.kt
new file mode 100644
index 000000000..9bc313393
--- /dev/null
+++ b/ring-android/app/src/main/java/cx/ring/fragments/GeneralAccountFragment.kt
@@ -0,0 +1,230 @@
+/*
+ *  Copyright (C) 2004-2021 Savoir-faire Linux Inc.
+ *
+ *  Author: Alexandre Lision <alexandre.lision@savoirfairelinux.com>
+ *          Adrien Béraud <adrien.beraud@savoirfairelinux.com>
+ *
+ *  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 cx.ring.fragments
+
+import android.app.Activity
+import android.content.Context
+import android.content.DialogInterface
+import android.os.Bundle
+import android.text.format.Formatter
+import android.util.Log
+import android.view.inputmethod.EditorInfo
+import androidx.appcompat.app.AlertDialog
+import androidx.preference.*
+import com.google.android.material.dialog.MaterialAlertDialogBuilder
+import cx.ring.R
+import cx.ring.account.AccountEditionFragment
+import cx.ring.mvp.BasePreferenceFragment
+import cx.ring.services.SharedPreferencesServiceImpl
+import cx.ring.views.EditTextIntegerPreference
+import cx.ring.views.EditTextPreferenceDialog
+import cx.ring.views.PasswordPreference
+import dagger.hilt.android.AndroidEntryPoint
+import net.jami.model.Account
+import net.jami.model.AccountConfig
+import net.jami.model.ConfigKey
+import net.jami.model.ConfigKey.Companion.fromString
+import net.jami.settings.GeneralAccountPresenter
+import net.jami.settings.GeneralAccountView
+import net.jami.utils.Tuple
+
+@AndroidEntryPoint
+class GeneralAccountFragment : BasePreferenceFragment<GeneralAccountPresenter>(), GeneralAccountView {
+    private val changeAccountStatusListener = Preference.OnPreferenceChangeListener { preference: Preference?, newValue: Any ->
+        presenter.setEnabled(newValue as Boolean)
+        false
+    }
+    private val changeBasicPreferenceListener = Preference.OnPreferenceChangeListener { preference: Preference, newValue: Any ->
+        Log.i(TAG, "Changing preference " + preference.key + " to value:" + newValue)
+        val key = fromString(preference.key) ?: return@OnPreferenceChangeListener false
+        if (preference is TwoStatePreference) {
+            presenter.twoStatePreferenceChanged(key, newValue)
+        } else if (preference is PasswordPreference) {
+            val tmp = StringBuilder()
+            var i = 0
+            while (i < (newValue as String).length) {
+                tmp.append("*")
+                ++i
+            }
+            preference.setSummary(tmp.toString())
+            presenter.passwordPreferenceChanged(key, newValue)
+        } else if (key === ConfigKey.ACCOUNT_USERNAME) {
+            presenter.userNameChanged(key, newValue)
+            preference.summary = newValue as CharSequence
+        } else {
+            preference.summary = newValue as CharSequence
+            presenter.preferenceChanged(key, newValue)
+        }
+        true
+    }
+
+    override fun accountChanged(account: Account) {
+        val pm = preferenceManager
+        pm.sharedPreferencesMode = Context.MODE_PRIVATE
+        pm.sharedPreferencesName = SharedPreferencesServiceImpl.PREFS_ACCOUNT + account.accountID
+        setPreferenceDetails(account.config)
+        val pref = findPreference<SwitchPreference>("Account.status")
+        if (account.isSip && pref != null) {
+            pref.title = account.alias
+            val status: String = getString(if (account.isEnabled) {
+                when {
+                    account.isTrying -> R.string.account_status_connecting
+                    account.needsMigration() -> R.string.account_update_needed
+                    account.isInError -> R.string.account_status_connection_error
+                    account.isRegistered -> R.string.account_status_online
+                    else -> R.string.account_status_unknown
+                }
+            } else {
+                R.string.account_status_offline
+            })
+            pref.summary = status
+            pref.isChecked = account.isEnabled
+
+            // An ip2ip account is always ready
+            pref.isEnabled = !account.isIP2IP
+            pref.onPreferenceChangeListener = changeAccountStatusListener
+        }
+        setPreferenceListener(account.config, changeBasicPreferenceListener)
+    }
+
+    override fun finish() {
+        activity?.onBackPressed()
+    }
+
+    override fun updateResolutions(maxResolution: Tuple<Int?, Int?>?, currentResolution: Int) {}
+    private fun getFileSizeSummary(size: Int, maxSize: Int): CharSequence {
+        return if (size == 0) {
+            getText(R.string.account_accept_files_never)
+        } else if (size == maxSize) {
+            getText(R.string.account_accept_files_always)
+        } else {
+            Formatter.formatFileSize(requireContext(), size * 1000L * 1000L)
+        }
+    }
+
+    override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
+        super.onCreatePreferences(savedInstanceState, rootKey)
+        val args = arguments
+        presenter.init(args?.getString(AccountEditionFragment.ACCOUNT_ID_KEY))
+        val filePref = findPreference<SeekBarPreference>("acceptIncomingFilesMaxSize")
+        if (filePref != null) {
+            filePref.onPreferenceChangeListener = Preference.OnPreferenceChangeListener { p: Preference, v: Any ->
+                val pref = p as SeekBarPreference
+                p.setSummary(getFileSizeSummary(v as Int, pref.max))
+                true
+            }
+            filePref.summary = getFileSizeSummary(filePref.value, filePref.max)
+        }
+        val deletePref = findPreference<Preference>("Account.delete")
+        if (deletePref != null) {
+            deletePref.onPreferenceClickListener = Preference.OnPreferenceClickListener {
+                val deleteDialog = createDeleteDialog()
+                deleteDialog.show()
+                false
+            }
+        }
+    }
+
+    override fun onDisplayPreferenceDialog(preference: Preference) {
+        val fragmentManager = parentFragmentManager
+        if (fragmentManager.findFragmentByTag(DIALOG_FRAGMENT_TAG) != null) {
+            return
+        }
+        if (preference is EditTextIntegerPreference) {
+            val f = EditTextPreferenceDialog.newInstance(preference.getKey(), EditorInfo.TYPE_CLASS_NUMBER)
+            f.setTargetFragment(this, 0)
+            f.show(fragmentManager, DIALOG_FRAGMENT_TAG)
+        } else if (preference is PasswordPreference) {
+            val f = EditTextPreferenceDialog.newInstance(
+                preference.getKey(),
+                EditorInfo.TYPE_CLASS_TEXT or EditorInfo.TYPE_TEXT_VARIATION_PASSWORD
+            )
+            f.setTargetFragment(this, 0)
+            f.show(fragmentManager, DIALOG_FRAGMENT_TAG)
+        } else {
+            super.onDisplayPreferenceDialog(preference)
+        }
+    }
+
+    private fun setPreferenceDetails(details: AccountConfig) {
+        for (confKey in details.keys) {
+            val pref = findPreference<Preference>(confKey.key()) ?: continue
+            if (!confKey.isTwoState) {
+                val `val` = details[confKey]
+                (pref as EditTextPreference).text = `val`
+                if (pref is PasswordPreference) {
+                    val tmp = StringBuilder()
+                    for (i in 0 until `val`.length) {
+                        tmp.append("*")
+                    }
+                    pref.setSummary(tmp.toString())
+                } else {
+                    pref.setSummary(`val`)
+                }
+            } else {
+                (pref as TwoStatePreference).isChecked = details.getBool(confKey)
+            }
+        }
+    }
+
+    private fun setPreferenceListener(details: AccountConfig, listener: Preference.OnPreferenceChangeListener) {
+        for (confKey in details.keys) {
+            val pref = findPreference<Preference>(confKey.key())
+            if (pref != null) {
+                pref.onPreferenceChangeListener = listener
+            }
+        }
+    }
+
+    override fun addJamiPreferences(accountId: String) {
+        val pm = preferenceManager
+        pm.sharedPreferencesMode = Context.MODE_PRIVATE
+        pm.sharedPreferencesName = SharedPreferencesServiceImpl.PREFS_ACCOUNT + accountId
+        addPreferencesFromResource(R.xml.account_prefs_jami)
+    }
+
+    override fun addSipPreferences() {
+        addPreferencesFromResource(R.xml.account_general_prefs)
+    }
+
+    private fun createDeleteDialog(): AlertDialog {
+        val alertDialog = MaterialAlertDialogBuilder(requireContext())
+            .setMessage(R.string.account_delete_dialog_message)
+            .setTitle(R.string.account_delete_dialog_title)
+            .setPositiveButton(R.string.menu_delete) { dialog: DialogInterface?, whichButton: Int -> presenter.removeAccount() }
+            .setNegativeButton(android.R.string.cancel, null)
+            .create()
+        val activity: Activity? = activity
+        if (activity != null) alertDialog.setOwnerActivity(activity)
+        return alertDialog
+    }
+
+    companion object {
+        val TAG = GeneralAccountFragment::class.simpleName!!
+        private const val DIALOG_FRAGMENT_TAG = "androidx.preference.PreferenceFragment.DIALOG"
+        fun newInstance(accountId: String): GeneralAccountFragment {
+            val bundle = Bundle()
+            bundle.putString(AccountEditionFragment.ACCOUNT_ID_KEY, accountId)
+            val generalAccountFragment = GeneralAccountFragment()
+            generalAccountFragment.arguments = bundle
+            return generalAccountFragment
+        }
+    }
+}
\ No newline at end of file
diff --git a/ring-android/app/src/main/java/cx/ring/fragments/GeneralAccountPresenter.java b/ring-android/app/src/main/java/cx/ring/fragments/GeneralAccountPresenter.java
deleted file mode 100644
index eb4b9b6ac..000000000
--- a/ring-android/app/src/main/java/cx/ring/fragments/GeneralAccountPresenter.java
+++ /dev/null
@@ -1,141 +0,0 @@
-/*
- *  Copyright (C) 2004-2021 Savoir-faire Linux Inc.
- *
- *  Author: Hadrien De Sousa <hadrien.desousa@savoirfairelinux.com>
- *  Author: Adrien Béraud <adrien.beraud@savoirfairelinux.com>
- *
- *  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, write to the Free Software
- *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-package cx.ring.fragments;
-
-import android.util.Log;
-
-import javax.inject.Inject;
-import javax.inject.Named;
-
-import net.jami.model.Account;
-import net.jami.model.ConfigKey;
-import net.jami.mvp.RootPresenter;
-import net.jami.services.AccountService;
-import net.jami.services.HardwareService;
-import net.jami.services.PreferencesService;
-
-import io.reactivex.rxjava3.core.Scheduler;
-
-public class GeneralAccountPresenter extends RootPresenter<GeneralAccountView> {
-
-    private static final String TAG = GeneralAccountPresenter.class.getSimpleName();
-
-    private final AccountService mAccountService;
-    private final HardwareService mHardwareService;
-    private final PreferencesService mPreferenceService;
-
-    @Inject
-    @Named("UiScheduler")
-    protected Scheduler mUiScheduler;
-
-    private Account mAccount;
-
-    @Inject
-    GeneralAccountPresenter(AccountService accountService, HardwareService hardwareService, PreferencesService preferencesService) {
-        this.mAccountService = accountService;
-        this.mHardwareService = hardwareService;
-        this.mPreferenceService = preferencesService;
-    }
-
-    // Init with current account
-    public void init() {
-        init(mAccountService.getCurrentAccount());
-    }
-
-    public void init(String accountId) {
-        init(mAccountService.getAccount(accountId));
-    }
-
-    private void init(Account account) {
-        mCompositeDisposable.clear();
-        mAccount = account;
-
-        if (account != null) {
-            if (account.isJami()) {
-                getView().addJamiPreferences(account.getAccountID());
-            } else {
-                getView().addSipPreferences();
-            }
-            getView().accountChanged(account);
-            mCompositeDisposable.add(mAccountService.getObservableAccount(account.getAccountID())
-                    .observeOn(mUiScheduler)
-                    .subscribe(acc -> getView().accountChanged(acc)));
-
-            mCompositeDisposable.add(mHardwareService.getMaxResolutions()
-                    .observeOn(mUiScheduler)
-                    .subscribe(res -> {
-                        if (res.first == null) {
-                            getView().updateResolutions(null, mPreferenceService.getResolution());
-                        } else {
-                            getView().updateResolutions(res, mPreferenceService.getResolution());
-                        }
-                    },
-                    e -> getView().updateResolutions(null, mPreferenceService.getResolution())));
-        } else {
-            Log.e(TAG, "init: No currentAccount available");
-            getView().finish();
-        }
-    }
-
-    void setEnabled(boolean enabled) {
-        mAccount.setEnabled(enabled);
-        mAccountService.setAccountEnabled(mAccount.getAccountID(), enabled);
-    }
-
-    public void twoStatePreferenceChanged(ConfigKey configKey, Object newValue) {
-        if (configKey == ConfigKey.ACCOUNT_ENABLE) {
-            setEnabled((Boolean) newValue);
-        } else {
-            mAccount.setDetail(configKey, newValue.toString());
-            updateAccount();
-        }
-    }
-
-    void passwordPreferenceChanged(ConfigKey configKey, Object newValue) {
-        if (mAccount.isSip()) {
-            mAccount.getCredentials().get(0).setDetail(configKey, newValue.toString());
-        }
-        updateAccount();
-    }
-
-    void userNameChanged(ConfigKey configKey, Object newValue) {
-        if (mAccount.isSip()) {
-            mAccount.setDetail(configKey, newValue.toString());
-            mAccount.getCredentials().get(0).setDetail(configKey, newValue.toString());
-        }
-        updateAccount();
-    }
-
-    void preferenceChanged(ConfigKey configKey, Object newValue) {
-        mAccount.setDetail(configKey, newValue.toString());
-        updateAccount();
-    }
-
-    private void updateAccount() {
-        mAccountService.setCredentials(mAccount.getAccountID(), mAccount.getCredentialsHashMapList());
-        mAccountService.setAccountDetails(mAccount.getAccountID(), mAccount.getDetails());
-    }
-
-    public void removeAccount() {
-        mAccountService.removeAccount(mAccount.getAccountID());
-    }
-
-}
diff --git a/ring-android/app/src/main/java/cx/ring/fragments/LinkDeviceFragment.java b/ring-android/app/src/main/java/cx/ring/fragments/LinkDeviceFragment.java
index 5f167d250..60420ddd3 100644
--- a/ring-android/app/src/main/java/cx/ring/fragments/LinkDeviceFragment.java
+++ b/ring-android/app/src/main/java/cx/ring/fragments/LinkDeviceFragment.java
@@ -49,7 +49,6 @@ import net.jami.model.Account;
 
 import cx.ring.R;
 import cx.ring.account.AccountEditionFragment;
-import cx.ring.application.JamiApplication;
 import cx.ring.databinding.FragLinkDeviceBinding;
 import cx.ring.mvp.BaseBottomSheetFragment;
 import cx.ring.utils.DeviceUtils;
diff --git a/ring-android/app/src/main/java/cx/ring/fragments/MediaPreferenceFragment.java b/ring-android/app/src/main/java/cx/ring/fragments/MediaPreferenceFragment.java
deleted file mode 100644
index e85f508d5..000000000
--- a/ring-android/app/src/main/java/cx/ring/fragments/MediaPreferenceFragment.java
+++ /dev/null
@@ -1,185 +0,0 @@
-/*
- *  Copyright (C) 2004-2021 Savoir-faire Linux Inc.
- *
- *  Author: Alexandre Savard <alexandre.savard@savoirfairelinux.com>
- *          Alexandre Lision <alexandre.lision@savoirfairelinux.com>
- *          Adrien Béraud <adrien.beraud@savoirfairelinux.com>
- *
- *  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, write to the Free Software
- *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-package cx.ring.fragments;
-
-
-import android.content.Intent;
-import android.os.Bundle;
-
-import androidx.annotation.NonNull;
-import androidx.preference.Preference;
-import androidx.preference.TwoStatePreference;
-
-import com.google.android.material.dialog.MaterialAlertDialogBuilder;
-
-import java.util.ArrayList;
-
-import cx.ring.R;
-import cx.ring.account.AccountEditionFragment;
-import cx.ring.application.JamiApplication;
-import cx.ring.client.RingtoneActivity;
-import net.jami.model.Account;
-import net.jami.model.AccountConfig;
-import net.jami.model.Codec;
-import net.jami.model.ConfigKey;
-import cx.ring.mvp.BasePreferenceFragment;
-import dagger.hilt.android.AndroidEntryPoint;
-
-@AndroidEntryPoint
-public class MediaPreferenceFragment extends BasePreferenceFragment<MediaPreferencePresenter> implements MediaPreferenceView {
-
-    public static final String TAG = MediaPreferenceFragment.class.getSimpleName();
-    private static final int SELECT_RINGTONE_PATH = 40;
-    private final Preference.OnPreferenceChangeListener changeVideoPreferenceListener = (preference, newValue) -> {
-        final ConfigKey key = ConfigKey.fromString(preference.getKey());
-        presenter.videoPreferenceChanged(key, newValue);
-        return true;
-    };
-    private CodecPreference audioCodecsPref = null;
-    private CodecPreference videoCodecsPref = null;
-    private final Preference.OnPreferenceChangeListener changeCodecListener = (preference, o) -> {
-        ArrayList<Long> audio = audioCodecsPref.getActiveCodecList();
-        ArrayList<Long> video = videoCodecsPref.getActiveCodecList();
-        ArrayList<Long> newOrder = new ArrayList<>(audio.size() + video.size());
-        newOrder.addAll(audio);
-        newOrder.addAll(video);
-        presenter.codecChanged(newOrder);
-        return true;
-    };
-
-    public static MediaPreferenceFragment newInstance(@NonNull String accountId) {
-        Bundle bundle = new Bundle();
-        bundle.putString(AccountEditionFragment.ACCOUNT_ID_KEY, accountId);
-        MediaPreferenceFragment mediaPreferenceFragment = new MediaPreferenceFragment();
-        mediaPreferenceFragment.setArguments(bundle);
-        return mediaPreferenceFragment;
-    }
-
-    @Override
-    public void onCreatePreferences(Bundle bundle, String rootKey) {
-        super.onCreatePreferences(bundle, rootKey);
-
-        String accountId = getArguments().getString(AccountEditionFragment.ACCOUNT_ID_KEY);
-
-        addPreferencesFromResource(R.xml.account_media_prefs);
-        audioCodecsPref = (CodecPreference) findPreference("Account.audioCodecs");
-        videoCodecsPref = (CodecPreference) findPreference("Account.videoCodecs");
-        Preference ringtonePref = findPreference("ringtone");
-        ringtonePref.setOnPreferenceClickListener(preference -> {
-            Intent i = new Intent(requireActivity(), RingtoneActivity.class);
-            i.putExtra(AccountEditionFragment.ACCOUNT_ID_KEY, accountId);
-            requireActivity().startActivity(i);
-            return true;
-        });
-
-        presenter.init(accountId);
-    }
-
-    @Override
-    public void accountChanged(Account account, ArrayList<Codec> audioCodec, ArrayList<Codec> videoCodec) {
-        if (account == null) {
-            return;
-        }
-        setPreferenceDetails(account.getConfig());
-        audioCodecsPref.setCodecs(audioCodec);
-        videoCodecsPref.setCodecs(videoCodec);
-
-        addPreferenceListener(ConfigKey.VIDEO_ENABLED, changeVideoPreferenceListener);
-        audioCodecsPref.setOnPreferenceChangeListener(changeCodecListener);
-        videoCodecsPref.setOnPreferenceChangeListener(changeCodecListener);
-    }
-
-    @Override
-    public void displayWrongFileFormatDialog() {
-        new MaterialAlertDialogBuilder(requireContext())
-                .setTitle(R.string.ringtone_error_title)
-                .setMessage(R.string.ringtone_error_format_not_supported)
-                .setPositiveButton(android.R.string.ok, null)
-                .show();
-    }
-
-    @Override
-    public void displayPermissionCameraDenied() {
-        new MaterialAlertDialogBuilder(requireContext())
-                .setTitle(R.string.permission_dialog_camera_title)
-                .setMessage(R.string.permission_dialog_camera_message)
-                .setCancelable(false)
-                .setPositiveButton(android.R.string.ok, (dialog, which) -> dialog.dismiss())
-                .show();
-    }
-
-    @Override
-    public void displayFileSearchDialog() {
-        Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
-        intent.addCategory(Intent.CATEGORY_OPENABLE);
-        intent.setType("audio/*");
-        startActivityForResult(intent, SELECT_RINGTONE_PATH);
-    }
-
-    @Override
-    public void refresh(Account account) {
-        if (account != null) {
-            setPreferenceDetails(account.getConfig());
-        }
-        if (null != getListView() && null != getListView().getAdapter()) {
-            getListView().getAdapter().notifyDataSetChanged();
-        }
-        if (null != videoCodecsPref) {
-            videoCodecsPref.refresh();
-        }
-        if (null != audioCodecsPref) {
-            audioCodecsPref.refresh();
-        }
-    }
-
-
-    private void setPreferenceDetails(AccountConfig details) {
-        for (ConfigKey confKey : details.getKeys()) {
-            Preference pref = findPreference(confKey.key());
-
-            if (pref != null) {
-                if (pref instanceof TwoStatePreference) {
-                    ((TwoStatePreference) pref).setChecked(details.getBool(confKey));
-                } else if (confKey == ConfigKey.ACCOUNT_DTMF_TYPE) {
-                    pref.setDefaultValue(details.get(confKey).contentEquals("overrtp") ? "RTP" : "SIP");
-                    pref.setSummary(details.get(confKey).contentEquals("overrtp") ? "RTP" : "SIP");
-                } else {
-                    pref.setSummary(details.get(confKey));
-                }
-            }
-        }
-    }
-
-    private void addPreferenceListener(AccountConfig details, Preference.OnPreferenceChangeListener listener) {
-        for (ConfigKey confKey : details.getKeys())
-            addPreferenceListener(confKey, listener);
-    }
-
-    private void addPreferenceListener(ConfigKey key, Preference.OnPreferenceChangeListener listener) {
-        Preference pref = findPreference(key.key());
-        if (pref != null) {
-            pref.setOnPreferenceChangeListener(listener);
-        }
-    }
-
-
-}
diff --git a/ring-android/app/src/main/java/cx/ring/fragments/MediaPreferenceFragment.kt b/ring-android/app/src/main/java/cx/ring/fragments/MediaPreferenceFragment.kt
new file mode 100644
index 000000000..b1b1843b2
--- /dev/null
+++ b/ring-android/app/src/main/java/cx/ring/fragments/MediaPreferenceFragment.kt
@@ -0,0 +1,159 @@
+/*
+ *  Copyright (C) 2004-2021 Savoir-faire Linux Inc.
+ *
+ *  Author: Alexandre Savard <alexandre.savard@savoirfairelinux.com>
+ *          Alexandre Lision <alexandre.lision@savoirfairelinux.com>
+ *          Adrien Béraud <adrien.beraud@savoirfairelinux.com>
+ *
+ *  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, write to the Free Software
+ *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+package cx.ring.fragments
+
+import android.content.DialogInterface
+import android.content.Intent
+import android.os.Bundle
+import androidx.preference.Preference
+import androidx.preference.TwoStatePreference
+import com.google.android.material.dialog.MaterialAlertDialogBuilder
+import cx.ring.R
+import cx.ring.account.AccountEditionFragment
+import cx.ring.client.RingtoneActivity
+import cx.ring.mvp.BasePreferenceFragment
+import dagger.hilt.android.AndroidEntryPoint
+import net.jami.model.Account
+import net.jami.model.AccountConfig
+import net.jami.model.Codec
+import net.jami.model.ConfigKey
+import net.jami.model.ConfigKey.Companion.fromString
+import net.jami.settings.MediaPreferencePresenter
+import net.jami.settings.MediaPreferenceView
+
+@AndroidEntryPoint
+class MediaPreferenceFragment : BasePreferenceFragment<MediaPreferencePresenter>(), MediaPreferenceView {
+    private val changeVideoPreferenceListener = Preference.OnPreferenceChangeListener { preference: Preference, newValue: Any ->
+        val key = fromString(preference.key)!!
+        presenter.videoPreferenceChanged(key, newValue)
+        true
+    }
+    private var audioCodecsPref: CodecPreference? = null
+    private var videoCodecsPref: CodecPreference? = null
+
+    private val changeCodecListener = Preference.OnPreferenceChangeListener { _, _ ->
+        val audio = audioCodecsPref!!.activeCodecList
+        val video = videoCodecsPref!!.activeCodecList
+        val newOrder = ArrayList<Long>(audio.size + video.size)
+        newOrder.addAll(audio)
+        newOrder.addAll(video)
+        presenter.codecChanged(newOrder)
+        true
+    }
+
+    override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
+        super.onCreatePreferences(savedInstanceState, rootKey)
+        val accountId = requireArguments().getString(AccountEditionFragment.ACCOUNT_ID_KEY)!!
+        addPreferencesFromResource(R.xml.account_media_prefs)
+        audioCodecsPref = findPreference("Account.audioCodecs")
+        videoCodecsPref = findPreference("Account.videoCodecs")
+        findPreference<Preference>("ringtone")?.apply {
+            onPreferenceClickListener = Preference.OnPreferenceClickListener {
+                val i = Intent(requireActivity(), RingtoneActivity::class.java)
+                i.putExtra(AccountEditionFragment.ACCOUNT_ID_KEY, accountId)
+                requireActivity().startActivity(i)
+                true
+            }
+        }
+        presenter.init(accountId)
+    }
+
+    override fun accountChanged(account: Account, audioCodec: ArrayList<Codec>, videoCodec: ArrayList<Codec>) {
+        setPreferenceDetails(account.config)
+        audioCodecsPref!!.setCodecs(audioCodec)
+        videoCodecsPref!!.setCodecs(videoCodec)
+        addPreferenceListener(ConfigKey.VIDEO_ENABLED, changeVideoPreferenceListener)
+        audioCodecsPref!!.onPreferenceChangeListener = changeCodecListener
+        videoCodecsPref!!.onPreferenceChangeListener = changeCodecListener
+    }
+
+    override fun displayWrongFileFormatDialog() {
+        MaterialAlertDialogBuilder(requireContext())
+            .setTitle(R.string.ringtone_error_title)
+            .setMessage(R.string.ringtone_error_format_not_supported)
+            .setPositiveButton(android.R.string.ok, null)
+            .show()
+    }
+
+    override fun displayPermissionCameraDenied() {
+        MaterialAlertDialogBuilder(requireContext())
+            .setTitle(R.string.permission_dialog_camera_title)
+            .setMessage(R.string.permission_dialog_camera_message)
+            .setCancelable(false)
+            .setPositiveButton(android.R.string.ok) { dialog: DialogInterface, which: Int -> dialog.dismiss() }
+            .show()
+    }
+
+    override fun displayFileSearchDialog() {
+        val intent = Intent(Intent.ACTION_OPEN_DOCUMENT)
+        intent.addCategory(Intent.CATEGORY_OPENABLE)
+        intent.type = "audio/*"
+        startActivityForResult(intent, SELECT_RINGTONE_PATH)
+    }
+
+    override fun refresh(account: Account) {
+        setPreferenceDetails(account.config)
+        if (null != listView && null != listView.adapter) {
+            listView.adapter!!.notifyDataSetChanged()
+        }
+        if (null != videoCodecsPref) {
+            videoCodecsPref!!.refresh()
+        }
+        if (null != audioCodecsPref) {
+            audioCodecsPref!!.refresh()
+        }
+    }
+
+    private fun setPreferenceDetails(details: AccountConfig) {
+        for (confKey in details.keys) {
+            val pref = findPreference<Preference>(confKey.key())
+            if (pref != null) {
+                if (pref is TwoStatePreference) {
+                    pref.isChecked = details.getBool(confKey)
+                } else if (confKey === ConfigKey.ACCOUNT_DTMF_TYPE) {
+                    pref.setDefaultValue(if (details[confKey].contentEquals("overrtp")) "RTP" else "SIP")
+                    pref.summary = if (details[confKey].contentEquals("overrtp")) "RTP" else "SIP"
+                } else {
+                    pref.summary = details[confKey]
+                }
+            }
+        }
+    }
+
+    private fun addPreferenceListener(details: AccountConfig, listener: Preference.OnPreferenceChangeListener) {
+        for (confKey in details.keys) addPreferenceListener(confKey, listener)
+    }
+
+    private fun addPreferenceListener(key: ConfigKey, listener: Preference.OnPreferenceChangeListener) {
+        findPreference<Preference>(key.key())?.onPreferenceChangeListener = listener
+    }
+
+    companion object {
+        val TAG = MediaPreferenceFragment::class.simpleName!!
+        private const val SELECT_RINGTONE_PATH = 40
+        fun newInstance(accountId: String): MediaPreferenceFragment {
+            val mediaPreferenceFragment = MediaPreferenceFragment()
+            mediaPreferenceFragment.arguments = Bundle().apply { putString(AccountEditionFragment.ACCOUNT_ID_KEY, accountId) }
+            return mediaPreferenceFragment
+        }
+    }
+}
\ No newline at end of file
diff --git a/ring-android/app/src/main/java/cx/ring/fragments/MediaPreferencePresenter.java b/ring-android/app/src/main/java/cx/ring/fragments/MediaPreferencePresenter.java
deleted file mode 100644
index 15085da4d..000000000
--- a/ring-android/app/src/main/java/cx/ring/fragments/MediaPreferencePresenter.java
+++ /dev/null
@@ -1,99 +0,0 @@
-/*
- *  Copyright (C) 2004-2021 Savoir-faire Linux Inc.
- *
- *  Author: Hadrien De Sousa <hadrien.desousa@savoirfairelinux.com>
- *
- *  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, write to the Free Software
- *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-package cx.ring.fragments;
-
-import android.util.Log;
-
-import java.util.ArrayList;
-
-import javax.inject.Inject;
-import javax.inject.Named;
-
-import net.jami.model.Account;
-import net.jami.model.Codec;
-import net.jami.model.ConfigKey;
-import net.jami.mvp.RootPresenter;
-import net.jami.services.AccountService;
-import net.jami.services.DeviceRuntimeService;
-
-import io.reactivex.rxjava3.core.Scheduler;
-
-public class MediaPreferencePresenter extends RootPresenter<MediaPreferenceView>
-{
-    public static final String TAG = MediaPreferencePresenter.class.getSimpleName();
-
-    protected AccountService mAccountService;
-    protected DeviceRuntimeService mDeviceRuntimeService;
-
-    private Account mAccount;
-
-    @Inject
-    @Named("UiScheduler")
-    protected Scheduler mUiScheduler;
-
-    @Inject
-    public MediaPreferencePresenter(AccountService accountService, DeviceRuntimeService deviceRuntimeService) {
-        this.mAccountService = accountService;
-        this.mDeviceRuntimeService = deviceRuntimeService;
-    }
-
-    @Override
-    public void unbindView() {
-        super.unbindView();
-    }
-
-    void init(String accountId) {
-        mAccount = mAccountService.getAccount(accountId);
-        mCompositeDisposable.clear();
-        mCompositeDisposable.add(mAccountService
-                .getObservableAccount(accountId)
-                .switchMapSingle(account -> mAccountService.getCodecList(accountId)
-                        .observeOn(mUiScheduler)
-                        .doOnSuccess(codecList -> {
-                            final ArrayList<Codec> audioCodec = new ArrayList<>();
-                            final ArrayList<Codec> videoCodec = new ArrayList<>();
-                            for (Codec codec : codecList) {
-                                if (codec.getType() == Codec.Type.AUDIO) {
-                                    audioCodec.add(codec);
-                                } else if (codec.getType() == Codec.Type.VIDEO) {
-                                    videoCodec.add(codec);
-                                }
-                            }
-                            getView().accountChanged(account, audioCodec, videoCodec);
-                        }))
-                .subscribe(l -> {}, e -> Log.e(TAG, "Error loading codec list")));
-    }
-
-
-    void codecChanged(ArrayList<Long> codecs) {
-        mAccountService.setActiveCodecList(mAccount.getAccountID(), codecs);
-    }
-
-    void videoPreferenceChanged(ConfigKey key, Object newValue) {
-            mAccount.setDetail(key, newValue.toString());
-            updateAccount();
-    }
-
-    private void updateAccount() {
-        mAccountService.setCredentials(mAccount.getAccountID(), mAccount.getCredentialsHashMapList());
-        mAccountService.setAccountDetails(mAccount.getAccountID(), mAccount.getDetails());
-    }
-
-}
diff --git a/ring-android/app/src/main/java/cx/ring/fragments/PluginHandlersListFragment.kt b/ring-android/app/src/main/java/cx/ring/fragments/PluginHandlersListFragment.kt
index 85d83be18..53d449fc0 100644
--- a/ring-android/app/src/main/java/cx/ring/fragments/PluginHandlersListFragment.kt
+++ b/ring-android/app/src/main/java/cx/ring/fragments/PluginHandlersListFragment.kt
@@ -22,11 +22,7 @@ class PluginHandlersListFragment : Fragment(), PluginListItemListener {
         mPath = ConversationPath.fromBundle(requireArguments())!!
     }
 
-    override fun onCreateView(
-        inflater: LayoutInflater,
-        container: ViewGroup?,
-        savedInstanceState: Bundle?
-    ): View {
+    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
         return FragPluginHandlersListBinding.inflate(inflater, container, false).also { b ->
             b.handlerList.setHasFixedSize(true)
             b.handlerList.adapter = PluginsListAdapter(
@@ -52,15 +48,15 @@ class PluginHandlersListFragment : Fragment(), PluginListItemListener {
     }
 
     override fun onPluginItemClicked(pluginDetails: PluginDetails) {
-        JamiService.toggleChatHandler(pluginDetails.getmHandlerId(), mPath.accountId, mPath.conversationId, pluginDetails.isEnabled)
+        JamiService.toggleChatHandler(pluginDetails.handlerId, mPath.accountId, mPath.conversationId, pluginDetails.isEnabled)
     }
 
     override fun onPluginEnabled(pluginDetails: PluginDetails) {
-        JamiService.toggleChatHandler(pluginDetails.getmHandlerId(), mPath.accountId, mPath.conversationId, pluginDetails.isEnabled)
+        JamiService.toggleChatHandler(pluginDetails.handlerId, mPath.accountId, mPath.conversationId, pluginDetails.isEnabled)
     }
 
     companion object {
-        const val TAG = "PluginListHandlers"
+        val TAG = PluginHandlersListFragment::class.simpleName!!
         fun newInstance(accountId: String, peerId: String): PluginHandlersListFragment {
             val fragment = PluginHandlersListFragment()
             fragment.arguments = ConversationPath.toBundle(accountId, peerId)
diff --git a/ring-android/app/src/main/java/cx/ring/fragments/SIPAccountCreationFragment.kt b/ring-android/app/src/main/java/cx/ring/fragments/SIPAccountCreationFragment.kt
index 8cf94046a..8b5942014 100644
--- a/ring-android/app/src/main/java/cx/ring/fragments/SIPAccountCreationFragment.kt
+++ b/ring-android/app/src/main/java/cx/ring/fragments/SIPAccountCreationFragment.kt
@@ -36,8 +36,8 @@ import cx.ring.R
 import cx.ring.databinding.FragAccSipCreateBinding
 import cx.ring.mvp.BaseSupportFragment
 import dagger.hilt.android.AndroidEntryPoint
-import net.jami.mvp.SIPCreationView
-import net.jami.wizard.SIPCreationPresenter
+import net.jami.account.SIPCreationView
+import net.jami.account.SIPCreationPresenter
 
 @AndroidEntryPoint
 class SIPAccountCreationFragment : BaseSupportFragment<SIPCreationPresenter, SIPCreationView>(),
@@ -77,10 +77,10 @@ class SIPAccountCreationFragment : BaseSupportFragment<SIPCreationPresenter, SIP
     private fun createSIPAccount(bypassWarnings: Boolean) {
         //orientation is locked during the create of account to avoid the destruction of the thread
         requireActivity().requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_LOCKED
-        val hostname = binding!!.hostname.text.toString()
-        val proxy = binding!!.proxy.text.toString()
-        val username = binding!!.username.text.toString()
-        val password = binding!!.password.text.toString()
+        val hostname = binding!!.hostname.text?.toString()
+        val proxy = binding!!.proxy.text?.toString()
+        val username = binding!!.username.text?.toString()
+        val password = binding!!.password.text?.toString()
         presenter.startCreation(hostname, proxy, username, password, bypassWarnings)
     }
 
diff --git a/ring-android/app/src/main/java/cx/ring/fragments/SecurityAccountFragment.java b/ring-android/app/src/main/java/cx/ring/fragments/SecurityAccountFragment.java
deleted file mode 100644
index 0d4a11e6b..000000000
--- a/ring-android/app/src/main/java/cx/ring/fragments/SecurityAccountFragment.java
+++ /dev/null
@@ -1,317 +0,0 @@
-/*
- *  Copyright (C) 2004-2021 Savoir-faire Linux Inc.
- *
- *  Author: Alexandre Lision <alexandre.lision@savoirfairelinux.com>
- *          Adrien Béraud <adrien.beraud@savoirfairelinux.com>
- *
- *  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 cx.ring.fragments;
-
-import android.app.Activity;
-import android.content.Context;
-import android.content.Intent;
-import android.graphics.drawable.Drawable;
-import android.net.Uri;
-import android.os.Bundle;
-
-import androidx.preference.EditTextPreference;
-import androidx.preference.ListPreference;
-import androidx.preference.Preference;
-import androidx.preference.PreferenceCategory;
-import androidx.preference.TwoStatePreference;
-
-import android.util.Pair;
-
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileNotFoundException;
-import java.security.cert.CertificateFactory;
-import java.util.ArrayList;
-import java.util.Scanner;
-
-import cx.ring.R;
-import cx.ring.account.AccountEditionFragment;
-import cx.ring.application.JamiApplication;
-import net.jami.model.AccountConfig;
-import net.jami.model.AccountCredentials;
-import net.jami.model.ConfigKey;
-import cx.ring.mvp.BasePreferenceFragment;
-import cx.ring.utils.AndroidFileUtils;
-import net.jami.utils.Tuple;
-import cx.ring.views.CredentialPreferenceDialog;
-import cx.ring.views.CredentialsPreference;
-import dagger.hilt.android.AndroidEntryPoint;
-
-@AndroidEntryPoint
-public class SecurityAccountFragment extends BasePreferenceFragment<SecurityAccountPresenter> implements SecurityAccountView {
-    public static final String TAG = SecurityAccountFragment.class.getSimpleName();
-
-    private static final String DIALOG_FRAGMENT_TAG = "android.support.v14.preference.PreferenceFragment.DIALOG";
-    private static final int SELECT_CA_LIST_RC = 42;
-    private static final int SELECT_PRIVATE_KEY_RC = 43;
-    private static final int SELECT_CERTIFICATE_RC = 44;
-
-    private PreferenceCategory credentialsCategory;
-    private PreferenceCategory tlsCategory;
-    private final Preference.OnPreferenceChangeListener editCredentialListener = (preference, newValue) -> {
-        // We need the old and new value to correctly edit the list of credentials
-        Pair<AccountCredentials, AccountCredentials> result = (Pair<AccountCredentials, AccountCredentials>) newValue;
-        presenter.credentialEdited(new Tuple<>(result.first, result.second));
-        return false;
-    };
-    private final Preference.OnPreferenceChangeListener addCredentialListener = (preference, newValue) -> {
-        Pair<AccountCredentials, AccountCredentials> result = (Pair<AccountCredentials, AccountCredentials>) newValue;
-        presenter.credentialAdded(new Tuple<>(result.first, result.second));
-        return false;
-    };
-    private final Preference.OnPreferenceClickListener filePickerListener = preference -> {
-        if (preference.getKey().contentEquals(ConfigKey.TLS_CA_LIST_FILE.key())) {
-            performFileSearch(SELECT_CA_LIST_RC);
-        }
-        if (preference.getKey().contentEquals(ConfigKey.TLS_PRIVATE_KEY_FILE.key())) {
-            performFileSearch(SELECT_PRIVATE_KEY_RC);
-        }
-        if (preference.getKey().contentEquals(ConfigKey.TLS_CERTIFICATE_FILE.key())) {
-            performFileSearch(SELECT_CERTIFICATE_RC);
-        }
-        return true;
-    };
-    private final Preference.OnPreferenceChangeListener tlsListener = (preference, newValue) -> {
-        ConfigKey key = ConfigKey.fromString(preference.getKey());
-
-        if (preference.getKey().contentEquals(ConfigKey.TLS_ENABLE.key())) {
-            if ((Boolean) newValue) {
-                presenter.tlsChanged(ConfigKey.STUN_ENABLE, false);
-            }
-        }
-
-        if (preference.getKey().contentEquals(ConfigKey.SRTP_KEY_EXCHANGE.key())) {
-            newValue = ((Boolean) newValue) ? "sdes" : "";
-        }
-
-        if (!(preference instanceof TwoStatePreference)) {
-            preference.setSummary((String) newValue);
-        }
-
-        presenter.tlsChanged(key, newValue);
-
-        return true;
-    };
-
-    @Override
-    public void onCreatePreferences(Bundle bundle, String s) {
-        super.onCreatePreferences(bundle, s);
-
-        addPreferencesFromResource(R.xml.account_security_prefs);
-        credentialsCategory = (PreferenceCategory) findPreference("Account.credentials");
-        credentialsCategory.findPreference("Add.credentials").setOnPreferenceChangeListener(addCredentialListener);
-        tlsCategory = (PreferenceCategory) findPreference("TLS.category");
-
-        presenter.init(getArguments().getString(AccountEditionFragment.ACCOUNT_ID_KEY));
-    }
-
-    @Override
-    public void onDisplayPreferenceDialog(Preference preference) {
-        if (getFragmentManager().findFragmentByTag(DIALOG_FRAGMENT_TAG) != null) {
-            return;
-        }
-        if (preference instanceof CredentialsPreference) {
-            CredentialPreferenceDialog preferenceDialog = CredentialPreferenceDialog.newInstance(preference.getKey());
-            preferenceDialog.setTargetFragment(this, 0);
-            preferenceDialog.show(getFragmentManager(), DIALOG_FRAGMENT_TAG);
-        } else {
-            super.onDisplayPreferenceDialog(preference);
-        }
-    }
-
-    @Override
-    public void addAllCredentials(ArrayList<AccountCredentials> credentials) {
-        int i = 0;
-        for (AccountCredentials cred : credentials) {
-            CredentialsPreference toAdd = new CredentialsPreference(getPreferenceManager().getContext());
-            toAdd.setKey("credential" + i);
-            toAdd.setPersistent(false);
-            toAdd.setCreds(cred);
-            toAdd.setIcon(null);
-            credentialsCategory.addPreference(toAdd);
-            i++;
-            toAdd.setOnPreferenceChangeListener(editCredentialListener);
-        }
-    }
-
-    @Override
-    public void removeAllCredentials() {
-        int i = 0;
-        while (true) {
-            Preference toRemove = credentialsCategory.findPreference("credential" + i);
-            if (toRemove == null) {
-                break;
-            }
-            credentialsCategory.removePreference(toRemove);
-            i++;
-        }
-    }
-
-    @Override
-    public void setDetails(AccountConfig config, String[] tlsMethods) {
-        for (int i = 0; i < tlsCategory.getPreferenceCount(); ++i) {
-            final Preference current = tlsCategory.getPreference(i);
-            final ConfigKey key = ConfigKey.fromString(current.getKey());
-
-            if (current instanceof TwoStatePreference) {
-                if (key == ConfigKey.SRTP_KEY_EXCHANGE) {
-                    ((TwoStatePreference) current).setChecked(config.get(key).equals("sdes"));
-                } else {
-                    ((TwoStatePreference) current).setChecked(config.getBool(key));
-                }
-            } else {
-                if (key == ConfigKey.TLS_CA_LIST_FILE) {
-                    File crt = new File(config.get(ConfigKey.TLS_CA_LIST_FILE));
-                    current.setSummary(crt.getName());
-                    setFeedbackIcon(current, crt);
-                    current.setOnPreferenceClickListener(filePickerListener);
-                } else if (key == ConfigKey.TLS_PRIVATE_KEY_FILE) {
-                    File pem = new File(config.get(ConfigKey.TLS_PRIVATE_KEY_FILE));
-                    current.setSummary(pem.getName());
-                    setFeedbackIcon(current, pem);
-                    current.setOnPreferenceClickListener(filePickerListener);
-                } else if (key == ConfigKey.TLS_CERTIFICATE_FILE) {
-                    File pem = new File(config.get(ConfigKey.TLS_CERTIFICATE_FILE));
-                    current.setSummary(pem.getName());
-                    setFeedbackIcon(current, pem);
-                    checkForRSAKey(pem);
-                    current.setOnPreferenceClickListener(filePickerListener);
-                } else if (key == ConfigKey.TLS_METHOD) {
-                    ListPreference listPref = (ListPreference) current;
-                    String curVal = config.get(key);
-                    listPref.setEntries(tlsMethods);
-                    listPref.setEntryValues(tlsMethods);
-                    listPref.setValue(curVal);
-                    current.setSummary(curVal);
-                } else if (current instanceof EditTextPreference) {
-                    String val = config.get(key);
-                    ((EditTextPreference) current).setText(val);
-                    current.setSummary(val);
-                } else {
-                    current.setSummary(config.get(key));
-                }
-            }
-
-            current.setOnPreferenceChangeListener(tlsListener);
-        }
-    }
-
-    public boolean checkCertificate(File f) {
-        try {
-            FileInputStream fis = new FileInputStream(f.getAbsolutePath());
-            CertificateFactory cf = CertificateFactory.getInstance("X509");
-            cf.generateCertificate(fis);
-            return true;
-        } catch (Exception e) {
-            return false;
-        }
-    }
-
-    public boolean findRSAKey(File f) {
-        // NOTE: This check is not complete but better than nothing.
-        try {
-            Scanner scanner = new Scanner(f);
-            while (scanner.hasNextLine()) {
-                if (scanner.nextLine().contains("-----BEGIN RSA PRIVATE KEY-----")) return true;
-            }
-        } catch(FileNotFoundException e) {}
-        return false;
-    }
-
-    private void checkForRSAKey(File f) {
-        if (findRSAKey(f)) {
-            tlsCategory.findPreference(ConfigKey.TLS_PRIVATE_KEY_FILE.key()).setEnabled(false);
-        } else {
-            tlsCategory.findPreference(ConfigKey.TLS_PRIVATE_KEY_FILE.key()).setEnabled(true);
-        }
-    }
-
-    private void setFeedbackIcon(Preference current, File certFile) {
-        Context c = current.getContext();
-        boolean isKey = current.getKey().contentEquals(ConfigKey.TLS_PRIVATE_KEY_FILE.key());
-        if (isKey && findRSAKey(certFile) || !isKey && checkCertificate(certFile)) {
-            Drawable icon = c.getDrawable(R.drawable.baseline_check_circle_24);
-            icon.setTint(c.getResources().getColor(R.color.green_500));
-            current.setIcon(icon);
-        } else {
-            Drawable icon = c.getDrawable(R.drawable.baseline_error_24);
-            icon.setTint(c.getResources().getColor(R.color.colorError));
-            current.setIcon(icon);
-        }
-    }
-
-    public void performFileSearch(int requestCodeToSet) {
-
-        // ACTION_OPEN_DOCUMENT is the intent to choose a file via the system's file
-        // browser.
-        Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
-
-        // Filter to only show results that can be "opened", such as a
-        // file (as opposed to a list of contacts or timezones)
-        intent.addCategory(Intent.CATEGORY_OPENABLE);
-
-        // Filter to show only images, using the image MIME data type.
-        // If one wanted to search for ogg vorbis files, the type would be "audio/ogg".
-        // To search for all documents available via installed storage providers,
-        // it would be "*/*".
-        intent.setType("*/*");
-        startActivityForResult(intent, requestCodeToSet);
-    }
-
-    public void onActivityResult(int requestCode, int resultCode, Intent data) {
-        if (resultCode == Activity.RESULT_CANCELED) {
-            return;
-        }
-
-        Uri uri = data.getData();
-        if (uri == null) {
-            return;
-        }
-        File myFile = new File(AndroidFileUtils.getRealPathFromURI(getContext(), uri));
-
-        ConfigKey key = null;
-        Preference preference;
-        switch (requestCode) {
-            case SELECT_CA_LIST_RC:
-                preference = tlsCategory.findPreference(ConfigKey.TLS_CA_LIST_FILE.key());
-                preference.setSummary(myFile.getName());
-                key = ConfigKey.TLS_CA_LIST_FILE;
-                setFeedbackIcon(preference, myFile);
-                break;
-            case SELECT_PRIVATE_KEY_RC:
-                preference = tlsCategory.findPreference(ConfigKey.TLS_PRIVATE_KEY_FILE.key());
-                preference.setSummary(myFile.getName());
-                key = ConfigKey.TLS_PRIVATE_KEY_FILE;
-                setFeedbackIcon(preference, myFile);
-                break;
-            case SELECT_CERTIFICATE_RC:
-                preference = tlsCategory.findPreference(ConfigKey.TLS_CERTIFICATE_FILE.key());
-                preference.setSummary(myFile.getName());
-                key = ConfigKey.TLS_CERTIFICATE_FILE;
-                setFeedbackIcon(preference, myFile);
-                checkForRSAKey(myFile);
-                break;
-            default:
-                break;
-        }
-
-        presenter.fileActivityResult(key, myFile.getAbsolutePath());
-    }
-}
diff --git a/ring-android/app/src/main/java/cx/ring/fragments/SecurityAccountFragment.kt b/ring-android/app/src/main/java/cx/ring/fragments/SecurityAccountFragment.kt
new file mode 100644
index 000000000..e8032c9d2
--- /dev/null
+++ b/ring-android/app/src/main/java/cx/ring/fragments/SecurityAccountFragment.kt
@@ -0,0 +1,281 @@
+/*
+ *  Copyright (C) 2004-2021 Savoir-faire Linux Inc.
+ *
+ *  Author: Alexandre Lision <alexandre.lision@savoirfairelinux.com>
+ *          Adrien Béraud <adrien.beraud@savoirfairelinux.com>
+ *
+ *  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 cx.ring.fragments
+
+import android.app.Activity
+import android.content.Intent
+import android.os.Bundle
+import android.util.Pair
+import androidx.preference.*
+import cx.ring.R
+import cx.ring.account.AccountEditionFragment
+import cx.ring.mvp.BasePreferenceFragment
+import cx.ring.utils.AndroidFileUtils
+import cx.ring.views.CredentialPreferenceDialog
+import cx.ring.views.CredentialsPreference
+import dagger.hilt.android.AndroidEntryPoint
+import net.jami.account.SecurityAccountPresenter
+import net.jami.account.SecurityAccountView
+import net.jami.model.AccountConfig
+import net.jami.model.AccountCredentials
+import net.jami.model.ConfigKey
+import java.io.File
+import java.io.FileInputStream
+import java.io.FileNotFoundException
+import java.security.cert.CertificateFactory
+import java.util.*
+
+@AndroidEntryPoint
+class SecurityAccountFragment : BasePreferenceFragment<SecurityAccountPresenter>(), SecurityAccountView {
+    private var credentialsCategory: PreferenceCategory? = null
+    private var tlsCategory: PreferenceCategory? = null
+    private val editCredentialListener = Preference.OnPreferenceChangeListener { preference: Preference?, newValue: Any ->
+        // We need the old and new value to correctly edit the list of credentials
+        val result = newValue as Pair<AccountCredentials, AccountCredentials>
+        presenter.credentialEdited(result.first, result.second)
+        false
+    }
+    private val addCredentialListener = Preference.OnPreferenceChangeListener { preference: Preference?, newValue: Any ->
+        val result = newValue as Pair<AccountCredentials, AccountCredentials>
+        presenter.credentialAdded(result.first, result.second)
+        false
+    }
+    private val filePickerListener = Preference.OnPreferenceClickListener { preference: Preference ->
+        if (preference.key.contentEquals(ConfigKey.TLS_CA_LIST_FILE.key())) {
+            performFileSearch(SELECT_CA_LIST_RC)
+        }
+        if (preference.key.contentEquals(ConfigKey.TLS_PRIVATE_KEY_FILE.key())) {
+            performFileSearch(SELECT_PRIVATE_KEY_RC)
+        }
+        if (preference.key.contentEquals(ConfigKey.TLS_CERTIFICATE_FILE.key())) {
+            performFileSearch(SELECT_CERTIFICATE_RC)
+        }
+        true
+    }
+    private val tlsListener = Preference.OnPreferenceChangeListener { preference: Preference, newValue: Any ->
+        var newValue = newValue
+        val key = ConfigKey.fromString(preference.key)
+        if (key == ConfigKey.TLS_ENABLE) {
+            if (newValue as Boolean) {
+                presenter.tlsChanged(ConfigKey.STUN_ENABLE, false)
+            }
+        }
+        if (key == ConfigKey.SRTP_KEY_EXCHANGE) {
+            newValue = if (newValue as Boolean) "sdes" else ""
+        }
+        if (preference !is TwoStatePreference) {
+            preference.summary = newValue as String
+        }
+        presenter.tlsChanged(key, newValue)
+        true
+    }
+
+    override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
+        super.onCreatePreferences(savedInstanceState, rootKey)
+        addPreferencesFromResource(R.xml.account_security_prefs)
+        credentialsCategory = findPreference<PreferenceCategory>("Account.credentials")?.apply {
+            findPreference<Preference>("Add.credentials")?.onPreferenceChangeListener = addCredentialListener
+        }
+        tlsCategory = findPreference("TLS.category")
+        presenter.init(requireArguments().getString(AccountEditionFragment.ACCOUNT_ID_KEY)!!)
+    }
+
+    override fun onDisplayPreferenceDialog(preference: Preference) {
+        if (parentFragmentManager.findFragmentByTag(DIALOG_FRAGMENT_TAG) != null) {
+            return
+        }
+        if (preference is CredentialsPreference) {
+            val preferenceDialog = CredentialPreferenceDialog.newInstance(preference.getKey())
+            preferenceDialog.setTargetFragment(this, 0)
+            preferenceDialog.show(parentFragmentManager, DIALOG_FRAGMENT_TAG)
+        } else {
+            super.onDisplayPreferenceDialog(preference)
+        }
+    }
+
+    override fun addAllCredentials(credentials: List<AccountCredentials>) {
+        for ((i, cred) in credentials.withIndex()) {
+            val toAdd = CredentialsPreference(preferenceManager.context).apply {
+                key = "credential$i"
+                isPersistent = false
+                creds = cred
+                icon = null
+            }
+            credentialsCategory!!.addPreference(toAdd)
+            toAdd.onPreferenceChangeListener = editCredentialListener
+        }
+    }
+
+    override fun removeAllCredentials() {
+        var i = 0
+        while (true) {
+            val toRemove = credentialsCategory!!.findPreference<Preference>("credential$i") ?: break
+            credentialsCategory!!.removePreference(toRemove)
+            i++
+        }
+    }
+
+    override fun setDetails(config: AccountConfig, tlsMethods: Array<String>) {
+        for (i in 0 until tlsCategory!!.preferenceCount) {
+            val current = tlsCategory!!.getPreference(i)
+            val key = ConfigKey.fromString(current.key)
+            if (current is TwoStatePreference) {
+                if (key === ConfigKey.SRTP_KEY_EXCHANGE) {
+                    current.isChecked = config[key] == "sdes"
+                } else {
+                    current.isChecked = config.getBool(key!!)
+                }
+            } else {
+                if (key === ConfigKey.TLS_CA_LIST_FILE) {
+                    val crt = File(config[ConfigKey.TLS_CA_LIST_FILE])
+                    current.summary = crt.name
+                    setFeedbackIcon(current, crt)
+                    current.onPreferenceClickListener = filePickerListener
+                } else if (key === ConfigKey.TLS_PRIVATE_KEY_FILE) {
+                    val pem = File(config[ConfigKey.TLS_PRIVATE_KEY_FILE])
+                    current.summary = pem.name
+                    setFeedbackIcon(current, pem)
+                    current.onPreferenceClickListener = filePickerListener
+                } else if (key === ConfigKey.TLS_CERTIFICATE_FILE) {
+                    val pem = File(config[ConfigKey.TLS_CERTIFICATE_FILE])
+                    current.summary = pem.name
+                    setFeedbackIcon(current, pem)
+                    checkForRSAKey(pem)
+                    current.onPreferenceClickListener = filePickerListener
+                } else if (key === ConfigKey.TLS_METHOD) {
+                    val listPref = current as ListPreference
+                    val curVal = config[key]
+                    listPref.entries = tlsMethods
+                    listPref.entryValues = tlsMethods
+                    listPref.value = curVal
+                    current.setSummary(curVal)
+                } else if (current is EditTextPreference) {
+                    val value = config[key!!]
+                    current.text = value
+                    current.setSummary(value)
+                } else {
+                    current.summary = config[key!!]
+                }
+            }
+            current.onPreferenceChangeListener = tlsListener
+        }
+    }
+
+    fun checkCertificate(f: File): Boolean {
+        return try {
+            val fis = FileInputStream(f.absolutePath)
+            val cf = CertificateFactory.getInstance("X509")
+            cf.generateCertificate(fis)
+            true
+        } catch (e: Exception) {
+            false
+        }
+    }
+
+    fun findRSAKey(f: File?): Boolean {
+        // NOTE: This check is not complete but better than nothing.
+        try {
+            val scanner = Scanner(f)
+            while (scanner.hasNextLine()) {
+                if (scanner.nextLine().contains("-----BEGIN RSA PRIVATE KEY-----")) return true
+            }
+        } catch (e: FileNotFoundException) {
+        }
+        return false
+    }
+
+    private fun checkForRSAKey(f: File) {
+        tlsCategory!!.findPreference<Preference>(ConfigKey.TLS_PRIVATE_KEY_FILE.key())!!.isEnabled = !findRSAKey(f)
+    }
+
+    private fun setFeedbackIcon(current: Preference?, certFile: File) {
+        val c = current!!.context
+        val isKey = ConfigKey.TLS_PRIVATE_KEY_FILE.key() == current.key
+        if (isKey && findRSAKey(certFile) || !isKey && checkCertificate(certFile)) {
+            val icon = c.getDrawable(R.drawable.baseline_check_circle_24)!!
+            icon.setTint(c.resources.getColor(R.color.green_500))
+            current.icon = icon
+        } else {
+            val icon = c.getDrawable(R.drawable.baseline_error_24)!!
+            icon.setTint(c.resources.getColor(R.color.colorError))
+            current.icon = icon
+        }
+    }
+
+    fun performFileSearch(requestCodeToSet: Int) {
+
+        // ACTION_OPEN_DOCUMENT is the intent to choose a file via the system's file
+        // browser.
+        val intent = Intent(Intent.ACTION_GET_CONTENT)
+
+        // Filter to only show results that can be "opened", such as a
+        // file (as opposed to a list of contacts or timezones)
+        intent.addCategory(Intent.CATEGORY_OPENABLE)
+
+        // Filter to show only images, using the image MIME data type.
+        // If one wanted to search for ogg vorbis files, the type would be "audio/ogg".
+        // To search for all documents available via installed storage providers,
+        // it would be "*/*".
+        intent.type = "*/*"
+        startActivityForResult(intent, requestCodeToSet)
+    }
+
+    override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
+        if (resultCode == Activity.RESULT_CANCELED) {
+            return
+        }
+        val uri = data!!.data ?: return
+        val myFile = File(AndroidFileUtils.getRealPathFromURI(requireContext(), uri))
+        var key: ConfigKey? = null
+        val preference: Preference?
+        when (requestCode) {
+            SELECT_CA_LIST_RC -> {
+                preference = tlsCategory!!.findPreference(ConfigKey.TLS_CA_LIST_FILE.key())
+                preference!!.summary = myFile.name
+                key = ConfigKey.TLS_CA_LIST_FILE
+                setFeedbackIcon(preference, myFile)
+            }
+            SELECT_PRIVATE_KEY_RC -> {
+                preference = tlsCategory!!.findPreference(ConfigKey.TLS_PRIVATE_KEY_FILE.key())
+                preference!!.summary = myFile.name
+                key = ConfigKey.TLS_PRIVATE_KEY_FILE
+                setFeedbackIcon(preference, myFile)
+            }
+            SELECT_CERTIFICATE_RC -> {
+                preference = tlsCategory!!.findPreference(ConfigKey.TLS_CERTIFICATE_FILE.key())
+                preference!!.summary = myFile.name
+                key = ConfigKey.TLS_CERTIFICATE_FILE
+                setFeedbackIcon(preference, myFile)
+                checkForRSAKey(myFile)
+            }
+            else -> {
+            }
+        }
+        presenter.fileActivityResult(key, myFile.absolutePath)
+    }
+
+    companion object {
+        val TAG = SecurityAccountFragment::class.java.simpleName
+        private const val DIALOG_FRAGMENT_TAG = "androidx.preference.PreferenceFragment.DIALOG"
+        private const val SELECT_CA_LIST_RC = 42
+        private const val SELECT_PRIVATE_KEY_RC = 43
+        private const val SELECT_CERTIFICATE_RC = 44
+    }
+}
\ No newline at end of file
diff --git a/ring-android/app/src/main/java/cx/ring/fragments/SecurityAccountPresenter.java b/ring-android/app/src/main/java/cx/ring/fragments/SecurityAccountPresenter.java
deleted file mode 100644
index 91be1c284..000000000
--- a/ring-android/app/src/main/java/cx/ring/fragments/SecurityAccountPresenter.java
+++ /dev/null
@@ -1,97 +0,0 @@
-/*
- *  Copyright (C) 2004-2021 Savoir-faire Linux Inc.
- *
- *  Author: Hadrien De Sousa <hadrien.desousa@savoirfairelinux.com>
- *
- *  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, write to the Free Software
- *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-package cx.ring.fragments;
-
-import java.util.List;
-
-import javax.inject.Inject;
-
-import net.jami.model.Account;
-import net.jami.model.AccountCredentials;
-import net.jami.model.ConfigKey;
-import net.jami.mvp.RootPresenter;
-import net.jami.services.AccountService;
-import net.jami.utils.Tuple;
-
-public class SecurityAccountPresenter extends RootPresenter<SecurityAccountView> {
-
-    protected AccountService mAccountService;
-
-    private Account mAccount;
-
-    @Inject
-    public SecurityAccountPresenter(AccountService accountService) {
-        this.mAccountService = accountService;
-    }
-
-    public void init(String accountId) {
-        mAccount = mAccountService.getAccount(accountId);
-        if (mAccount != null) {
-            getView().removeAllCredentials();
-            getView().addAllCredentials(mAccount.getCredentials());
-
-            List<String> methods = mAccountService.getTlsSupportedMethods();
-            String[] tlsMethods = methods.toArray(new String[methods.size()]);
-
-            getView().setDetails(mAccount.getConfig(), tlsMethods);
-        }
-    }
-
-    public void credentialEdited(Tuple<AccountCredentials, AccountCredentials> result) {
-        mAccount.removeCredential(result.first);
-        if (result.second != null) {
-            // There is a new value for this credentials it means it has been edited (otherwise deleted)
-            mAccount.addCredential(result.second);
-        }
-
-        mAccountService.setCredentials(mAccount.getAccountID(), mAccount.getCredentialsHashMapList());
-        mAccountService.setAccountDetails(mAccount.getAccountID(), mAccount.getDetails());
-
-        getView().removeAllCredentials();
-        getView().addAllCredentials(mAccount.getCredentials());
-    }
-
-    public void credentialAdded(Tuple<AccountCredentials, AccountCredentials> result) {
-        mAccount.addCredential(result.second);
-
-        mAccountService.setCredentials(mAccount.getAccountID(), mAccount.getCredentialsHashMapList());
-        mAccountService.setAccountDetails(mAccount.getAccountID(), mAccount.getDetails());
-
-        getView().removeAllCredentials();
-        getView().addAllCredentials(mAccount.getCredentials());
-    }
-
-    public void tlsChanged(ConfigKey key, Object newValue) {
-        if (newValue instanceof Boolean) {
-            mAccount.setDetail(key, (Boolean) newValue);
-        } else {
-            mAccount.setDetail(key, (String) newValue);
-        }
-
-        mAccountService.setCredentials(mAccount.getAccountID(), mAccount.getCredentialsHashMapList());
-        mAccountService.setAccountDetails(mAccount.getAccountID(), mAccount.getDetails());
-    }
-
-    public void fileActivityResult(ConfigKey key, String filePath) {
-        mAccount.setDetail(key, filePath);
-        mAccountService.setCredentials(mAccount.getAccountID(), mAccount.getCredentialsHashMapList());
-        mAccountService.setAccountDetails(mAccount.getAccountID(), mAccount.getDetails());
-    }
-}
diff --git a/ring-android/app/src/main/java/cx/ring/fragments/ShareWithFragment.kt b/ring-android/app/src/main/java/cx/ring/fragments/ShareWithFragment.kt
index f8b53a6f6..12b1f22f5 100644
--- a/ring-android/app/src/main/java/cx/ring/fragments/ShareWithFragment.kt
+++ b/ring-android/app/src/main/java/cx/ring/fragments/ShareWithFragment.kt
@@ -62,20 +62,20 @@ class ShareWithFragment : Fragment() {
 
     override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
         adapter = SmartListAdapter(null, object : SmartListListeners {
-            override fun onItemClick(smartListViewModel: SmartListViewModel) {
+            override fun onItemClick(item: SmartListViewModel) {
                 mPendingIntent?.let { intent ->
                     mPendingIntent = null
                     val type = intent.type
                     if (type != null && type.startsWith("text/")) {
                         intent.putExtra(Intent.EXTRA_TEXT, binding!!.previewText.text.toString())
                     }
-                    intent.putExtras(ConversationPath.toBundle(smartListViewModel.accountId, smartListViewModel.uri))
+                    intent.putExtras(ConversationPath.toBundle(item.accountId, item.uri))
                     intent.setClass(requireActivity(), ConversationActivity::class.java)
                     startActivity(intent)
                 }
             }
 
-            override fun onItemLongClick(smartListViewModel: SmartListViewModel) {}
+            override fun onItemLongClick(item: SmartListViewModel) {}
         }, mDisposable)
 
         val binding = FragSharewithBinding.inflate(inflater).apply {
diff --git a/ring-android/app/src/main/java/cx/ring/fragments/SmartListFragment.kt b/ring-android/app/src/main/java/cx/ring/fragments/SmartListFragment.kt
index d797f6f46..0b377baad 100644
--- a/ring-android/app/src/main/java/cx/ring/fragments/SmartListFragment.kt
+++ b/ring-android/app/src/main/java/cx/ring/fragments/SmartListFragment.kt
@@ -233,8 +233,8 @@ class SmartListFragment : BaseSupportFragment<SmartListPresenter, SmartListView>
         presenter.removeConversation(conversationUri)
     }
 
-    override fun clearConversation(callContact: Uri) {
-        presenter.clearConversation(callContact)
+    override fun clearConversation(conversationUri: Uri) {
+        presenter.clearConversation(conversationUri)
     }
 
     override fun copyContactNumberToClipboard(contactNumber: String) {
@@ -246,10 +246,6 @@ class SmartListFragment : BaseSupportFragment<SmartListPresenter, SmartListView>
         Snackbar.make(binding!!.listCoordinator, snackbarText, Snackbar.LENGTH_LONG).show()
     }
 
-    fun onFabButtonClicked() {
-        presenter.fabButtonClicked()
-    }
-
     override fun displayChooseNumberDialog(numbers: Array<CharSequence>) {
         val context = requireContext()
         MaterialAlertDialogBuilder(context)
@@ -297,12 +293,12 @@ class SmartListFragment : BaseSupportFragment<SmartListPresenter, SmartListView>
         }
     }
 
-    override fun displayClearDialog(uri: Uri) {
-        ActionHelper.launchClearAction(activity, uri, this@SmartListFragment)
+    override fun displayClearDialog(conversationUri: Uri) {
+        ActionHelper.launchClearAction(activity, conversationUri, this@SmartListFragment)
     }
 
-    override fun displayDeleteDialog(uri: Uri) {
-        ActionHelper.launchDeleteAction(activity, uri, this@SmartListFragment)
+    override fun displayDeleteDialog(conversationUri: Uri) {
+        ActionHelper.launchDeleteAction(activity, conversationUri, this@SmartListFragment)
     }
 
     override fun copyNumber(uri: Uri) {
@@ -381,12 +377,12 @@ class SmartListFragment : BaseSupportFragment<SmartListPresenter, SmartListView>
         binding?.apply { confsList.scrollToPosition(0) }
     }
 
-    override fun onItemClick(smartListViewModel: SmartListViewModel) {
-        presenter.conversationClicked(smartListViewModel)
+    override fun onItemClick(item: SmartListViewModel) {
+        presenter.conversationClicked(item)
     }
 
-    override fun onItemLongClick(smartListViewModel: SmartListViewModel) {
-        presenter.conversationLongClicked(smartListViewModel)
+    override fun onItemLongClick(item: SmartListViewModel) {
+        presenter.conversationLongClicked(item)
     }
 
     private fun changeSeparatorHeight(open: Boolean) {
diff --git a/ring-android/app/src/main/java/cx/ring/interfaces/BackHandlerInterface.java b/ring-android/app/src/main/java/cx/ring/interfaces/BackHandlerInterface.kt
similarity index 88%
rename from ring-android/app/src/main/java/cx/ring/interfaces/BackHandlerInterface.java
rename to ring-android/app/src/main/java/cx/ring/interfaces/BackHandlerInterface.kt
index e07eb5abf..64877e121 100644
--- a/ring-android/app/src/main/java/cx/ring/interfaces/BackHandlerInterface.java
+++ b/ring-android/app/src/main/java/cx/ring/interfaces/BackHandlerInterface.kt
@@ -16,8 +16,8 @@
  *  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 cx.ring.interfaces;
+package cx.ring.interfaces
 
-public interface BackHandlerInterface {
-    boolean onBackPressed();
-}
+interface BackHandlerInterface {
+    fun onBackPressed(): Boolean
+}
\ No newline at end of file
diff --git a/ring-android/app/src/main/java/cx/ring/interfaces/Colorable.java b/ring-android/app/src/main/java/cx/ring/interfaces/Colorable.java
deleted file mode 100644
index eab2ea90c..000000000
--- a/ring-android/app/src/main/java/cx/ring/interfaces/Colorable.java
+++ /dev/null
@@ -1,7 +0,0 @@
-package cx.ring.interfaces;
-
-import androidx.annotation.ColorInt;
-
-public interface Colorable {
-    void setColor(@ColorInt int color);
-}
diff --git a/ring-android/app/src/main/java/cx/ring/interfaces/Colorable.kt b/ring-android/app/src/main/java/cx/ring/interfaces/Colorable.kt
new file mode 100644
index 000000000..3b0adbf56
--- /dev/null
+++ b/ring-android/app/src/main/java/cx/ring/interfaces/Colorable.kt
@@ -0,0 +1,7 @@
+package cx.ring.interfaces
+
+import androidx.annotation.ColorInt
+
+interface Colorable {
+    fun setColor(@ColorInt color: Int)
+}
\ No newline at end of file
diff --git a/ring-android/app/src/main/java/cx/ring/mvp/BaseActivity.java b/ring-android/app/src/main/java/cx/ring/mvp/BaseActivity.java
index 05954aa1f..ae3c9527f 100644
--- a/ring-android/app/src/main/java/cx/ring/mvp/BaseActivity.java
+++ b/ring-android/app/src/main/java/cx/ring/mvp/BaseActivity.java
@@ -20,6 +20,8 @@
 package cx.ring.mvp;
 
 import android.os.Bundle;
+
+import androidx.annotation.Nullable;
 import androidx.appcompat.app.AppCompatActivity;
 
 import net.jami.mvp.RootPresenter;
@@ -31,7 +33,7 @@ public abstract class BaseActivity<T extends RootPresenter> extends AppCompatAct
     protected T presenter;
 
     @Override
-    public void onCreate(Bundle savedInstanceState) {
+    public void onCreate(@Nullable Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
 
         presenter.bindView(this);
diff --git a/ring-android/app/src/main/java/cx/ring/mvp/BaseBottomSheetFragment.java b/ring-android/app/src/main/java/cx/ring/mvp/BaseBottomSheetFragment.java
index fe91a9472..6cfb6df83 100644
--- a/ring-android/app/src/main/java/cx/ring/mvp/BaseBottomSheetFragment.java
+++ b/ring-android/app/src/main/java/cx/ring/mvp/BaseBottomSheetFragment.java
@@ -21,10 +21,10 @@ package cx.ring.mvp;
 
 import android.os.Bundle;
 import android.view.View;
-import android.widget.Toast;
 
 import androidx.annotation.IdRes;
 import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
 import androidx.fragment.app.Fragment;
 
 import com.google.android.material.bottomsheet.BottomSheetDialogFragment;
@@ -32,11 +32,9 @@ import com.google.android.material.bottomsheet.BottomSheetDialogFragment;
 import javax.inject.Inject;
 
 import cx.ring.R;
-import net.jami.model.Error;
-import net.jami.mvp.BaseView;
 import net.jami.mvp.RootPresenter;
 
-public abstract class BaseBottomSheetFragment<T extends RootPresenter> extends BottomSheetDialogFragment implements BaseView {
+public abstract class BaseBottomSheetFragment<T extends RootPresenter> extends BottomSheetDialogFragment {
 
     protected static final String TAG = BaseBottomSheetFragment.class.getSimpleName();
 
@@ -44,7 +42,7 @@ public abstract class BaseBottomSheetFragment<T extends RootPresenter> extends B
     protected T presenter;
 
     @Override
-    public void onViewCreated(@NonNull View view, Bundle savedInstanceState) {
+    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
         //Be sure to do the injection in onCreateView method
         if (presenter != null) {
             presenter.bindView(this);
@@ -59,34 +57,11 @@ public abstract class BaseBottomSheetFragment<T extends RootPresenter> extends B
             presenter.unbindView();
     }
 
-    public void displayErrorToast(Error error) {
-        String errorString;
-        switch (error) {
-            case NO_INPUT:
-                errorString = getString(R.string.call_error_no_camera_no_microphone);
-                break;
-            case INVALID_FILE:
-                errorString = getString(R.string.invalid_file);
-                break;
-            case NOT_ABLE_TO_WRITE_FILE:
-                errorString = getString(R.string.not_able_to_write_file);
-                break;
-            case NO_SPACE_LEFT:
-                errorString = getString(R.string.no_space_left_on_device);
-                break;
-            default:
-                errorString = getString(R.string.generic_error);
-                break;
-        }
-
-        Toast.makeText(requireContext(), errorString, Toast.LENGTH_LONG).show();
-    }
-
     protected void initPresenter(T presenter) {
     }
 
     protected void replaceFragmentWithSlide(Fragment fragment, @IdRes int content) {
-        getFragmentManager()
+        getParentFragmentManager()
                 .beginTransaction()
                 .setCustomAnimations(R.anim.slide_in_right,
                         R.anim.slide_out_left, R.anim.slide_in_left, R.anim.slide_out_right)
@@ -96,7 +71,7 @@ public abstract class BaseBottomSheetFragment<T extends RootPresenter> extends B
     }
 
     protected void replaceFragment(Fragment fragment, @IdRes int content) {
-        getFragmentManager()
+        getParentFragmentManager()
                 .beginTransaction()
                 .replace(content, fragment, TAG)
                 .addToBackStack(TAG)
diff --git a/ring-android/app/src/main/java/cx/ring/mvp/BasePreferenceFragment.java b/ring-android/app/src/main/java/cx/ring/mvp/BasePreferenceFragment.java
index 60e3749d0..7d783d456 100644
--- a/ring-android/app/src/main/java/cx/ring/mvp/BasePreferenceFragment.java
+++ b/ring-android/app/src/main/java/cx/ring/mvp/BasePreferenceFragment.java
@@ -21,20 +21,19 @@ package cx.ring.mvp;
 
 import android.os.Bundle;
 
+import androidx.annotation.Nullable;
 import androidx.preference.PreferenceFragmentCompat;
 
 import net.jami.mvp.RootPresenter;
 
 import javax.inject.Inject;
 
-import dagger.hilt.android.AndroidEntryPoint;
-
 public abstract class BasePreferenceFragment<T extends RootPresenter> extends PreferenceFragmentCompat {
     @Inject
     protected T presenter;
 
     @Override
-    public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
+    public void onCreatePreferences(@Nullable Bundle savedInstanceState, @Nullable String rootKey) {
         //Be sure to do the injection in onCreateView method
         presenter.bindView(this);
     }
diff --git a/ring-android/app/src/main/java/cx/ring/mvp/BaseSupportFragment.kt b/ring-android/app/src/main/java/cx/ring/mvp/BaseSupportFragment.kt
index 752b8b5b0..5b52dc8af 100644
--- a/ring-android/app/src/main/java/cx/ring/mvp/BaseSupportFragment.kt
+++ b/ring-android/app/src/main/java/cx/ring/mvp/BaseSupportFragment.kt
@@ -21,16 +21,13 @@ package cx.ring.mvp
 
 import android.os.Bundle
 import android.view.View
-import android.widget.Toast
 import androidx.annotation.IdRes
 import androidx.fragment.app.Fragment
 import cx.ring.R
-import net.jami.model.Error
-import net.jami.mvp.BaseView
 import net.jami.mvp.RootPresenter
 import javax.inject.Inject
 
-abstract class BaseSupportFragment<T : RootPresenter<V>, V> : Fragment(), BaseView {
+abstract class BaseSupportFragment<T : RootPresenter<in V>, in V> : Fragment() {
     @Inject lateinit
     var presenter: T
 
@@ -50,40 +47,26 @@ abstract class BaseSupportFragment<T : RootPresenter<V>, V> : Fragment(), BaseVi
         presenter.onDestroy()
     }
 
-    override fun displayErrorToast(error: Error) {
-        val errorString: String = when (error) {
-            Error.NO_INPUT -> getString(R.string.call_error_no_camera_no_microphone)
-            Error.INVALID_FILE -> getString(R.string.invalid_file)
-            Error.NOT_ABLE_TO_WRITE_FILE -> getString(R.string.not_able_to_write_file)
-            Error.NO_SPACE_LEFT -> getString(R.string.no_space_left_on_device)
-            else -> getString(R.string.generic_error)
-        }
-        Toast.makeText(requireContext(), errorString, Toast.LENGTH_LONG).show()
-    }
-
     protected open fun initPresenter(presenter: T) {}
 
-    protected fun replaceFragmentWithSlide(fragment: Fragment?, @IdRes content: Int) {
+    protected fun replaceFragmentWithSlide(fragment: Fragment, @IdRes content: Int) {
         parentFragmentManager
             .beginTransaction()
-            .setCustomAnimations(
-                R.anim.slide_in_right,
-                R.anim.slide_out_left, R.anim.slide_in_left, R.anim.slide_out_right
-            )
-            .replace(content, fragment!!, TAG)
+            .setCustomAnimations(R.anim.slide_in_right, R.anim.slide_out_left, R.anim.slide_in_left, R.anim.slide_out_right)
+            .replace(content, fragment, TAG)
             .addToBackStack(TAG)
             .commit()
     }
 
-    protected fun replaceFragment(fragment: Fragment?, @IdRes content: Int) {
+    protected fun replaceFragment(fragment: Fragment, @IdRes content: Int) {
         parentFragmentManager
             .beginTransaction()
-            .replace(content, fragment!!, TAG)
+            .replace(content, fragment, TAG)
             .addToBackStack(TAG)
             .commit()
     }
 
     companion object {
-        protected val TAG = BaseSupportFragment::class.simpleName
+        protected val TAG = BaseSupportFragment::class.simpleName!!
     }
 }
\ No newline at end of file
diff --git a/ring-android/app/src/main/java/cx/ring/plugins/PluginUtils.java b/ring-android/app/src/main/java/cx/ring/plugins/PluginUtils.java
deleted file mode 100644
index f5eb793cd..000000000
--- a/ring-android/app/src/main/java/cx/ring/plugins/PluginUtils.java
+++ /dev/null
@@ -1,187 +0,0 @@
-package cx.ring.plugins;
-
-import android.content.Context;
-
-import androidx.annotation.NonNull;
-
-import java.io.File;
-import java.util.ArrayList;
-import java.util.List;
-
-import cx.ring.settings.pluginssettings.PluginDetails;
-
-import net.jami.daemon.JamiService;
-import net.jami.daemon.StringMap;
-import net.jami.utils.Log;
-
-public class PluginUtils {
-
-    public static final String TAG = PluginUtils.class.getSimpleName();
-
-    /**
-     * Fetches the plugins folder in the internal storage for plugins subfolder
-     * Gathers the details of each plugin in a PluginDetails instance
-     * @param mContext The current context
-     * @return List of PluginDetails
-     */
-    public static List<PluginDetails> getInstalledPlugins(Context mContext){
-        tree(mContext.getFilesDir() + File.separator+ "plugins",0);
-        tree(mContext.getCacheDir().getAbsolutePath(),0);
-
-        List<String> pluginsPaths = JamiService.getInstalledPlugins();
-        List<String> loadedPluginsPaths = JamiService.getLoadedPlugins();
-
-        List<PluginDetails> pluginsList = new ArrayList<>(pluginsPaths.size());
-        for (String pluginPath : pluginsPaths) {
-            File pluginFolder = new File(pluginPath);
-            if(pluginFolder.isDirectory()) {
-                pluginsList.add(new PluginDetails(
-                        pluginFolder.getName(),
-                        pluginFolder.getAbsolutePath(), loadedPluginsPaths.contains(pluginPath)));
-            }
-        }
-        return pluginsList;
-    }
-
-    /**
-     * Fetches the plugins folder in the internal storage for plugins subfolder
-     * Gathers the details of each plugin in a PluginDetails instance
-     * @param mContext The current context
-     * @param accountId The current account id
-     * @param peerId The current conversation peer id
-     * @return List of PluginDetails
-     */
-    public static List<PluginDetails> getChatHandlersDetails(Context mContext, String accountId, String peerId){
-        tree(mContext.getFilesDir() + File.separator+ "plugins",0);
-        tree(mContext.getCacheDir().getAbsolutePath(),0);
-
-        List<String> chatHandlersId = JamiService.getChatHandlers();
-        List<String> chatHandlerStatus = JamiService.getChatHandlerStatus(accountId, peerId);
-
-        List<PluginDetails> handlersList = new ArrayList<>(chatHandlersId.size());
-        for (String handlerId : chatHandlersId) {
-            StringMap handlerDetails = JamiService.getChatHandlerDetails(handlerId);
-            String pluginPath = handlerDetails.get("pluginId");
-            pluginPath = pluginPath.substring(0, pluginPath.lastIndexOf("/data"));
-            boolean enabled = false;
-
-            if (chatHandlerStatus.contains(handlerId)) {
-                enabled = true;
-            }
-            handlersList.add(new PluginDetails(
-                    handlerDetails.get("name"),
-                    pluginPath, enabled, handlerId));
-        }
-        return handlersList;
-    }
-
-    /**
-     * Loads the so file and instantiates the plugin init function (toggle on)
-     * @param path root path of the plugin
-     * @return true if loaded
-     */
-    public static boolean loadPlugin(String path) {
-        return JamiService.loadPlugin(path);
-    }
-
-    /**
-     * Toggles the plugin off (destroying any objects created by the plugin)
-     * then unloads the so file
-     * @param path root path of the plugin
-     * @return true if unloaded
-     */
-    public static boolean unloadPlugin(String path) {
-        return JamiService.unloadPlugin(path);
-    }
-
-    /**
-     * Lists the root paths of the loaded plugins
-     * @return list of path
-     */
-    public static List<String> getLoadedPlugins() {
-        return JamiService.getLoadedPlugins();
-    }
-
-    /**
-     * Displays the content of any directory
-     * @param dirPath directory to display
-     * @param level default 0, exists because the function is recursive
-     */
-    public static void tree(String dirPath,  int level) {
-        String repeated = new String(new char[level]).replace("\0", "\t|");
-        File file = new File(dirPath);
-        if(file.exists()) {
-            Log.d(TAG, "|"+ repeated + "-- " + file.getName());
-            if(file.isDirectory()) {
-                File[] files = file.listFiles();
-                if (files != null && files.length > 0) {
-                    for(File f : files) {
-                        tree(f.getAbsolutePath(),level+1);
-                    }
-                }
-            }
-        }
-    }
-
-    /**
-     * Useful Util method that is available in for android api >=  24
-     * We emulate it here
-     *
-     * @param input        input object that can be null
-     * @param defaultValue default NonNull object of the same type as input
-     * @return input if not null, defaultValue otherwise
-     */
-    public static <T> T getOrElse(T input, @NonNull T defaultValue) {
-        if (input == null) {
-            return defaultValue;
-        } else {
-            return input;
-        }
-    }
-
-    /**
-     *
-     * @param listString List<String>
-     * @return String of the form String entries = "[AAA,BBB,CCC]"
-     */
-    public static String listStringToStringList(List<String> listString) {
-        StringBuilder stringBuilder = new StringBuilder();
-
-        if(!listString.isEmpty()) {
-            for(int i=0; i< listString.size(); i++){
-                stringBuilder.append(listString.get(i)).append(",");
-            }
-            stringBuilder.append(listString.get(listString.size()));
-        }
-
-        return stringBuilder.toString();
-    }
-
-    /**
-     * Converts a string that contains a list to a java List<String>
-     * E.g: String entries = "[AAA,BBB,CCC]" to List<String> l, where l.get(0) = "AAA"
-     * @return List of strings
-     * @param stringList a string in the form "[AAA,BBB,CCC]"
-     */
-    public static List<String> stringListToListString(String stringList) {
-        List<String> listString = new ArrayList<>();
-        StringBuilder currentWord = new StringBuilder();
-        if(!stringList.isEmpty()) {
-            for (int i = 0; i < stringList.length(); i++) {
-                char currentChar = stringList.charAt(i);
-                if (currentChar != ',') {
-                    currentWord.append(currentChar);
-                } else {
-                    listString.add(currentWord.toString());
-                    currentWord = new StringBuilder();
-                }
-
-                if (i == stringList.length() - 1) {
-                    listString.add(currentWord.toString());
-                    break;
-                }
-            }
-        }
-        return listString;
-    }
-}
diff --git a/ring-android/app/src/main/java/cx/ring/plugins/PluginUtils.kt b/ring-android/app/src/main/java/cx/ring/plugins/PluginUtils.kt
new file mode 100644
index 000000000..553065f11
--- /dev/null
+++ b/ring-android/app/src/main/java/cx/ring/plugins/PluginUtils.kt
@@ -0,0 +1,140 @@
+package cx.ring.plugins
+
+import android.content.Context
+import android.util.Log
+import cx.ring.settings.pluginssettings.PluginDetails
+import cx.ring.plugins.PluginUtils
+import net.jami.daemon.JamiService
+import net.jami.daemon.StringMap
+import java.io.File
+import java.lang.StringBuilder
+import java.util.ArrayList
+
+object PluginUtils {
+    val TAG = PluginUtils::class.simpleName!!
+
+    /**
+     * Fetches the plugins folder in the internal storage for plugins subfolder
+     * Gathers the details of each plugin in a PluginDetails instance
+     * @param mContext The current context
+     * @return List of PluginDetails
+     */
+    @JvmStatic
+    fun getInstalledPlugins(mContext: Context): List<PluginDetails> {
+        tree(mContext.filesDir.toString() + File.separator + "plugins", 0)
+        tree(mContext.cacheDir.absolutePath, 0)
+        val pluginsPaths: List<String> = JamiService.getInstalledPlugins()
+        val loadedPluginsPaths: List<String> = JamiService.getLoadedPlugins()
+        val pluginsList: MutableList<PluginDetails> = ArrayList(pluginsPaths.size)
+        for (pluginPath in pluginsPaths) {
+            val pluginFolder = File(pluginPath)
+            if (pluginFolder.isDirectory) {
+                pluginsList.add(
+                    PluginDetails(
+                        pluginFolder.name,
+                        pluginFolder.absolutePath,
+                        loadedPluginsPaths.contains(pluginPath)
+                    )
+                )
+            }
+        }
+        return pluginsList
+    }
+
+    /**
+     * Fetches the plugins folder in the internal storage for plugins subfolder
+     * Gathers the details of each plugin in a PluginDetails instance
+     * @param mContext The current context
+     * @param accountId The current account id
+     * @param peerId The current conversation peer id
+     * @return List of PluginDetails
+     */
+    fun getChatHandlersDetails(mContext: Context, accountId: String, peerId: String): List<PluginDetails> {
+        tree(mContext.filesDir.toString() + File.separator + "plugins", 0)
+        tree(mContext.cacheDir.absolutePath, 0)
+        val chatHandlersId: List<String> = JamiService.getChatHandlers()
+        val chatHandlerStatus: List<String> = JamiService.getChatHandlerStatus(accountId, peerId)
+        val handlersList: MutableList<PluginDetails> = ArrayList(chatHandlersId.size)
+        for (handlerId in chatHandlersId) {
+            val handlerDetails = JamiService.getChatHandlerDetails(handlerId)
+            var pluginPath = handlerDetails["pluginId"]
+            pluginPath = pluginPath!!.substring(0, pluginPath.lastIndexOf("/data"))
+            var enabled = false
+            if (chatHandlerStatus.contains(handlerId)) {
+                enabled = true
+            }
+            handlersList.add(PluginDetails(handlerDetails["name"]!!, pluginPath, enabled, handlerId))
+        }
+        return handlersList
+    }
+
+    /**
+     * Loads the so file and instantiates the plugin init function (toggle on)
+     * @param path root path of the plugin
+     * @return true if loaded
+     */
+    @JvmStatic
+    fun loadPlugin(path: String?): Boolean {
+        return JamiService.loadPlugin(path)
+    }
+
+    /**
+     * Toggles the plugin off (destroying any objects created by the plugin)
+     * then unloads the so file
+     * @param path root path of the plugin
+     * @return true if unloaded
+     */
+    @JvmStatic
+    fun unloadPlugin(path: String?): Boolean {
+        return JamiService.unloadPlugin(path)
+    }
+
+    /**
+     * Displays the content of any directory
+     * @param dirPath directory to display
+     * @param level default 0, exists because the function is recursive
+     */
+    fun tree(dirPath: String, level: Int) {
+        val repeated = String(CharArray(level)).replace("\u0000", "\t|")
+        val file = File(dirPath)
+        if (file.exists()) {
+            Log.d(TAG, "|" + repeated + "-- " + file.name)
+            if (file.isDirectory) {
+                val files = file.listFiles()
+                if (files != null && files.isNotEmpty()) {
+                    for (f in files) {
+                        tree(f.absolutePath, level + 1)
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * Converts a string that contains a list to a java List<String>
+     * E.g: String entries = "[AAA,BBB,CCC]" to List<String> l, where l.get(0) = "AAA"
+     * @return List of strings
+     * @param stringList a string in the form "[AAA,BBB,CCC]"
+    </String></String> */
+    @JvmStatic
+    fun stringListToListString(stringList: String): List<String> {
+        val listString: MutableList<String> = ArrayList()
+        val currentWord = StringBuilder()
+        if (stringList.isNotEmpty()) {
+            for (i in stringList.indices) {
+                val currentChar = stringList[i]
+                if (currentChar != ',') {
+                    currentWord.append(currentChar)
+                } else {
+                    listString.add(currentWord.toString())
+                    currentWord.clear()
+                }
+                if (i == stringList.length - 1) {
+                    listString.add(currentWord.toString())
+                    break
+                }
+            }
+        }
+        return listString
+    }
+}
\ No newline at end of file
diff --git a/ring-android/app/src/main/java/cx/ring/plugins/RecyclerPicker/RecyclerPickerLayoutManager.java b/ring-android/app/src/main/java/cx/ring/plugins/RecyclerPicker/RecyclerPickerLayoutManager.java
deleted file mode 100644
index 60d66b076..000000000
--- a/ring-android/app/src/main/java/cx/ring/plugins/RecyclerPicker/RecyclerPickerLayoutManager.java
+++ /dev/null
@@ -1,100 +0,0 @@
-package cx.ring.plugins.RecyclerPicker;
-
-import android.content.Context;
-import android.view.View;
-
-import androidx.recyclerview.widget.LinearLayoutManager;
-import androidx.recyclerview.widget.LinearSnapHelper;
-import androidx.recyclerview.widget.RecyclerView;
-
-public class RecyclerPickerLayoutManager extends LinearLayoutManager {
-    private RecyclerView recyclerView;
-    private final ItemSelectedListener listener;
-
-    public RecyclerPickerLayoutManager(Context context, int orientation, boolean reverseLayout, ItemSelectedListener listener) {
-        super(context, orientation, reverseLayout);
-        this.listener = listener;
-    }
-
-    @Override
-    public void onAttachedToWindow(RecyclerView view) {
-        super.onAttachedToWindow(view);
-        recyclerView = view;
-
-        // Smart snapping
-        LinearSnapHelper snapHelper = new LinearSnapHelper();
-        snapHelper.attachToRecyclerView(recyclerView);
-    }
-
-    @Override
-    public void onLayoutCompleted(RecyclerView.State state) {
-        super.onLayoutCompleted(state);
-        scaleDownView();
-    }
-
-
-    @Override
-    public int scrollHorizontallyBy(int dx, RecyclerView.Recycler recycler,
-                                    RecyclerView.State state) {
-        int scrolled = super.scrollHorizontallyBy(dx, recycler, state);
-
-        if (getOrientation() == VERTICAL) {
-            return 0;
-        } else {
-            scaleDownView();
-            return scrolled;
-        }
-    }
-
-    @Override
-    public void onScrollStateChanged(int state) {
-        super.onScrollStateChanged(state);
-        // When scroll stops we notify on the selected item
-        if (state == RecyclerView.SCROLL_STATE_IDLE) {
-
-            // Find the closest child to the recyclerView center --> this is the selected item.
-            int recyclerViewCenterX = getRecyclerViewCenterX();
-            int minDistance = recyclerView.getWidth();
-            int position = -1;
-            for (int i=0; i< recyclerView.getChildCount(); i++) {
-                View child = recyclerView.getChildAt(i);
-                int childCenterX = getDecoratedLeft(child) + (getDecoratedRight(child) - getDecoratedLeft(child)) / 2;
-                int newDistance = Math.abs(childCenterX - recyclerViewCenterX);
-                if (newDistance < minDistance) {
-                    minDistance = newDistance;
-                    position = recyclerView.getChildLayoutPosition(child);
-                }
-            }
-            listener.onItemSelected(position);
-        }
-    }
-
-    private int getRecyclerViewCenterX() {
-        return recyclerView.getWidth()/2 + recyclerView.getLeft();
-    }
-
-    private void scaleDownView() {
-        float mid = getWidth() / 2.0f;
-        for (int i=0; i<getChildCount(); i++) {
-
-            // Calculating the distance of the child from the center
-            View child = getChildAt(i);
-            float childMid = (getDecoratedLeft(child) + getDecoratedRight(child)) / 2.0f;
-            float distanceFromCenter = Math.abs(mid - childMid);
-
-            // The scaling formula
-            float k = (float) Math.sqrt((double)(distanceFromCenter/getWidth()));
-            k *= 1.5f;
-            float scale = 1-k*0.66f;
-
-            // Set scale to view
-            child.setScaleX(scale);
-            child.setScaleY(scale);
-        }
-    }
-
-    public interface ItemSelectedListener {
-        void onItemSelected(int position);
-        void onItemClicked(int position);
-    }
-}
diff --git a/ring-android/app/src/main/java/cx/ring/plugins/RecyclerPicker/RecyclerPickerLayoutManager.kt b/ring-android/app/src/main/java/cx/ring/plugins/RecyclerPicker/RecyclerPickerLayoutManager.kt
new file mode 100644
index 000000000..757aecb4b
--- /dev/null
+++ b/ring-android/app/src/main/java/cx/ring/plugins/RecyclerPicker/RecyclerPickerLayoutManager.kt
@@ -0,0 +1,92 @@
+package cx.ring.plugins.RecyclerPicker
+
+import android.content.Context
+import androidx.recyclerview.widget.LinearLayoutManager
+import androidx.recyclerview.widget.RecyclerView
+import androidx.recyclerview.widget.LinearSnapHelper
+import androidx.recyclerview.widget.RecyclerView.Recycler
+
+class RecyclerPickerLayoutManager(
+    context: Context?,
+    orientation: Int,
+    reverseLayout: Boolean,
+    private val listener: ItemSelectedListener
+) : LinearLayoutManager(context, orientation, reverseLayout) {
+    private var recyclerView: RecyclerView? = null
+    override fun onAttachedToWindow(view: RecyclerView) {
+        super.onAttachedToWindow(view)
+        recyclerView = view
+
+        // Smart snapping
+        val snapHelper = LinearSnapHelper()
+        snapHelper.attachToRecyclerView(recyclerView)
+    }
+
+    override fun onLayoutCompleted(state: RecyclerView.State) {
+        super.onLayoutCompleted(state)
+        scaleDownView()
+    }
+
+    override fun scrollHorizontallyBy(
+        dx: Int, recycler: Recycler,
+        state: RecyclerView.State
+    ): Int {
+        val scrolled = super.scrollHorizontallyBy(dx, recycler, state)
+        return if (orientation == VERTICAL) {
+            0
+        } else {
+            scaleDownView()
+            scrolled
+        }
+    }
+
+    override fun onScrollStateChanged(state: Int) {
+        super.onScrollStateChanged(state)
+        // When scroll stops we notify on the selected item
+        if (state == RecyclerView.SCROLL_STATE_IDLE) {
+
+            // Find the closest child to the recyclerView center --> this is the selected item.
+            val recyclerViewCenterX = recyclerViewCenterX
+            var minDistance = recyclerView!!.width
+            var position = -1
+            for (i in 0 until recyclerView!!.childCount) {
+                val child = recyclerView!!.getChildAt(i)
+                val childCenterX = getDecoratedLeft(child) + (getDecoratedRight(child) - getDecoratedLeft(child)) / 2
+                val newDistance = Math.abs(childCenterX - recyclerViewCenterX)
+                if (newDistance < minDistance) {
+                    minDistance = newDistance
+                    position = recyclerView!!.getChildLayoutPosition(child)
+                }
+            }
+            listener.onItemSelected(position)
+        }
+    }
+
+    private val recyclerViewCenterX: Int
+        private get() = recyclerView!!.width / 2 + recyclerView!!.left
+
+    private fun scaleDownView() {
+        val mid = width / 2.0f
+        for (i in 0 until childCount) {
+
+            // Calculating the distance of the child from the center
+            val child = getChildAt(i)
+            val childMid = (getDecoratedLeft(child!!) + getDecoratedRight(child)) / 2.0f
+            val distanceFromCenter = Math.abs(mid - childMid)
+
+            // The scaling formula
+            var k = Math.sqrt((distanceFromCenter / width).toDouble()).toFloat()
+            k *= 1.5f
+            val scale = 1 - k * 0.66f
+
+            // Set scale to view
+            child.scaleX = scale
+            child.scaleY = scale
+        }
+    }
+
+    interface ItemSelectedListener {
+        fun onItemSelected(position: Int)
+        fun onItemClicked(position: Int)
+    }
+}
\ No newline at end of file
diff --git a/ring-android/app/src/main/java/cx/ring/service/BootReceiver.java b/ring-android/app/src/main/java/cx/ring/service/BootReceiver.java
deleted file mode 100644
index 40cc0153b..000000000
--- a/ring-android/app/src/main/java/cx/ring/service/BootReceiver.java
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
- *  Copyright (C) 2004-2021 Savoir-faire Linux Inc.
- *
- *  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, write to the Free Software
- *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-package cx.ring.service;
-
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.text.format.DateUtils;
-import android.util.Log;
-
-import androidx.core.content.ContextCompat;
-
-import javax.inject.Inject;
-
-import cx.ring.application.JamiApplication;
-import dagger.hilt.android.AndroidEntryPoint;
-
-import net.jami.services.PreferencesService;
-
-@AndroidEntryPoint
-public class BootReceiver extends BroadcastReceiver {
-    private static final String TAG = BootReceiver.class.getSimpleName();
-
-    @Inject
-    PreferencesService mPreferencesService;
-
-    public BootReceiver() {}
-
-    @Override
-    public void onReceive(Context context, Intent intent) {
-        if (intent == null || intent.getAction() == null)
-            return;
-        final String action = intent.getAction();
-        if (Intent.ACTION_BOOT_COMPLETED.equals(action) ||
-                Intent.ACTION_REBOOT.equals(action) ||
-                Intent.ACTION_MY_PACKAGE_REPLACED.equals(action))
-        {
-            try {
-                //((JamiApplication) context.getApplicationContext()).getInjectionComponent().inject(this);
-                if (mPreferencesService.getSettings().isAllowOnStartup()) {
-                    try {
-                        ContextCompat.startForegroundService(context, new Intent(SyncService.ACTION_START)
-                                .setClass(context, SyncService.class)
-                                .putExtra(SyncService.EXTRA_TIMEOUT, 5 * DateUtils.SECOND_IN_MILLIS));
-                    } catch (IllegalStateException e) {
-                        Log.e(TAG, "Error starting service", e);
-                    }
-                }
-            } catch (Exception e) {
-                Log.e(TAG, "Can't start on boot", e);
-            }
-        }
-    }
-}
diff --git a/ring-android/app/src/main/java/cx/ring/service/BootReceiver.kt b/ring-android/app/src/main/java/cx/ring/service/BootReceiver.kt
new file mode 100644
index 000000000..1c09f5dfb
--- /dev/null
+++ b/ring-android/app/src/main/java/cx/ring/service/BootReceiver.kt
@@ -0,0 +1,57 @@
+/*
+ *  Copyright (C) 2004-2021 Savoir-faire Linux Inc.
+ *
+ *  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, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+package cx.ring.service
+
+import android.content.BroadcastReceiver
+import android.content.Context
+import android.content.Intent
+import android.text.format.DateUtils
+import android.util.Log
+import androidx.core.content.ContextCompat
+import dagger.hilt.android.AndroidEntryPoint
+import net.jami.services.PreferencesService
+import javax.inject.Inject
+
+@AndroidEntryPoint
+class BootReceiver : BroadcastReceiver() {
+    @Inject
+    lateinit var mPreferencesService: PreferencesService
+
+    override fun onReceive(context: Context, intent: Intent) {
+        val action = intent.action ?: return
+        if (Intent.ACTION_BOOT_COMPLETED == action || Intent.ACTION_REBOOT == action || Intent.ACTION_MY_PACKAGE_REPLACED == action) {
+            try {
+                if (mPreferencesService.settings.isAllowOnStartup) {
+                    try {
+                        ContextCompat.startForegroundService(context, Intent(SyncService.ACTION_START)
+                                .setClass(context, SyncService::class.java)
+                                .putExtra(SyncService.EXTRA_TIMEOUT, 5 * DateUtils.SECOND_IN_MILLIS))
+                    } catch (e: IllegalStateException) {
+                        Log.e(TAG, "Error starting service", e)
+                    }
+                }
+            } catch (e: Exception) {
+                Log.e(TAG, "Can't start on boot", e)
+            }
+        }
+    }
+
+    companion object {
+        private val TAG = BootReceiver::class.simpleName!!
+    }
+}
\ No newline at end of file
diff --git a/ring-android/app/src/main/java/cx/ring/service/CallNotificationService.java b/ring-android/app/src/main/java/cx/ring/service/CallNotificationService.java
deleted file mode 100644
index 522754d08..000000000
--- a/ring-android/app/src/main/java/cx/ring/service/CallNotificationService.java
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- *  Copyright (C) 2004-2021 Savoir-faire Linux Inc.
- *
- *  Author: Rayan Osseiran <rayan.osseiran@savoirfairelinux.com>
- *  Author: Adrien Béraud <adrien.beraud@savoirfairelinux.com>
- *
- *  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, write to the Free Software
- *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-package cx.ring.service;
-
-import android.app.Notification;
-import android.app.Service;
-import android.content.Intent;
-import android.content.pm.ServiceInfo;
-import android.os.Build;
-import android.os.IBinder;
-
-import androidx.annotation.Nullable;
-
-import javax.inject.Inject;
-
-import cx.ring.services.NotificationServiceImpl;
-import dagger.hilt.android.AndroidEntryPoint;
-
-import net.jami.services.NotificationService;
-
-@AndroidEntryPoint
-public class CallNotificationService extends Service {
-    public static final String ACTION_START = "START";
-    public static final String ACTION_STOP = "STOP";
-
-    @Inject
-    NotificationService mNotificationService;
-
-    @Override
-    public int onStartCommand(Intent intent, int flags, int startId) {
-        super.onStartCommand(intent, flags, startId);
-        if (ACTION_START.equals(intent.getAction())) {
-            Notification notification = (Notification) mNotificationService.showCallNotification(intent.getIntExtra(NotificationService.KEY_NOTIFICATION_ID, -1));
-            if (notification != null) {
-                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q)
-                    startForeground(NotificationServiceImpl.NOTIF_CALL_ID, notification, ServiceInfo.FOREGROUND_SERVICE_TYPE_PHONE_CALL | ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PROJECTION);
-                else
-                    startForeground(NotificationServiceImpl.NOTIF_CALL_ID, notification);
-            }
-        } else if (ACTION_STOP.equals(intent.getAction())) {
-            stopForeground(true);
-            stopSelf();
-            mNotificationService.cancelCallNotification();
-        }
-        return START_NOT_STICKY;
-    }
-
-    @Nullable
-    @Override
-    public IBinder onBind(Intent intent) {
-        return null;
-    }
-}
\ No newline at end of file
diff --git a/ring-android/app/src/main/java/cx/ring/service/CallNotificationService.kt b/ring-android/app/src/main/java/cx/ring/service/CallNotificationService.kt
new file mode 100644
index 000000000..fe9c19514
--- /dev/null
+++ b/ring-android/app/src/main/java/cx/ring/service/CallNotificationService.kt
@@ -0,0 +1,64 @@
+/*
+ *  Copyright (C) 2004-2021 Savoir-faire Linux Inc.
+ *
+ *  Author: Rayan Osseiran <rayan.osseiran@savoirfairelinux.com>
+ *  Author: Adrien Béraud <adrien.beraud@savoirfairelinux.com>
+ *
+ *  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, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+package cx.ring.service
+
+import android.app.Notification
+import android.app.Service
+import android.content.Intent
+import android.content.pm.ServiceInfo
+import android.os.Build
+import android.os.IBinder
+import cx.ring.services.NotificationServiceImpl
+import dagger.hilt.android.AndroidEntryPoint
+import net.jami.services.NotificationService
+import javax.inject.Inject
+
+@AndroidEntryPoint
+class CallNotificationService : Service() {
+    @Inject
+    lateinit var mNotificationService: NotificationService
+    override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int {
+        super.onStartCommand(intent, flags, startId)
+        if (ACTION_START == intent.action) {
+            val notification = mNotificationService.showCallNotification(intent.getIntExtra(NotificationService.KEY_NOTIFICATION_ID, -1)) as Notification?
+            if (notification != null) {
+                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q)
+                    startForeground(NotificationServiceImpl.NOTIF_CALL_ID, notification, ServiceInfo.FOREGROUND_SERVICE_TYPE_PHONE_CALL or ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PROJECTION)
+                else
+                    startForeground(NotificationServiceImpl.NOTIF_CALL_ID, notification)
+            }
+        } else if (ACTION_STOP == intent.action) {
+            stopForeground(true)
+            stopSelf()
+            mNotificationService.cancelCallNotification()
+        }
+        return START_NOT_STICKY
+    }
+
+    override fun onBind(intent: Intent): IBinder? {
+        return null
+    }
+
+    companion object {
+        const val ACTION_START = "START"
+        const val ACTION_STOP = "STOP"
+    }
+}
\ No newline at end of file
diff --git a/ring-android/app/src/main/java/cx/ring/service/OutgoingCallHandler.java b/ring-android/app/src/main/java/cx/ring/service/OutgoingCallHandler.java
deleted file mode 100644
index 8194d9698..000000000
--- a/ring-android/app/src/main/java/cx/ring/service/OutgoingCallHandler.java
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- *  Copyright (C) 2004-2021 Savoir-faire Linux Inc.
- *
- *  Author: Adrien Béraud <adrien.beraud@savoirfairelinux.com>
- *
- *  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, write to the Free Software
- *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-package cx.ring.service;
-
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.SharedPreferences;
-import android.preference.PreferenceManager;
-
-import cx.ring.R;
-import cx.ring.client.CallActivity;
-import net.jami.model.Uri;
-
-public class OutgoingCallHandler extends BroadcastReceiver {
-    public static final String KEY_CACHE_HAVE_RINGACCOUNT = "cache_haveRingAccount";
-    public static final String KEY_CACHE_HAVE_SIPACCOUNT = "cache_haveSipAccount";
-    private static final String TAG = OutgoingCallHandler.class.getSimpleName();
-
-    @Override
-    public void onReceive(Context context, Intent intent) {
-        if (intent == null || !Intent.ACTION_NEW_OUTGOING_CALL.equals(intent.getAction()))
-            return;
-
-        String phoneNumber = getResultData();
-        if (phoneNumber == null)
-            phoneNumber = intent.getStringExtra(Intent.EXTRA_PHONE_NUMBER);
-
-        SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context);
-        boolean systemDialer = sharedPreferences.getBoolean(context.getString(R.string.pref_systemDialer_key), false);
-        if (systemDialer) {
-            boolean systemDialerSip = sharedPreferences.getBoolean(KEY_CACHE_HAVE_SIPACCOUNT, false);
-            boolean systemDialerRing = sharedPreferences.getBoolean(KEY_CACHE_HAVE_RINGACCOUNT, false);
-
-            Uri uri = Uri.fromString(phoneNumber);
-            boolean isRingId = uri.isHexId();
-            if ((!isRingId && systemDialerSip) || (isRingId && systemDialerRing) || uri.isSingleIp()) {
-                Intent i = new Intent(Intent.ACTION_CALL)
-                        .setClass(context, CallActivity.class)
-                        .setData(android.net.Uri.parse(phoneNumber))
-                        .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-                context.startActivity(i);
-                setResultData(null);
-            }
-        }
-    }
-}
diff --git a/ring-android/app/src/main/java/cx/ring/service/OutgoingCallHandler.kt b/ring-android/app/src/main/java/cx/ring/service/OutgoingCallHandler.kt
new file mode 100644
index 000000000..3a738d1b1
--- /dev/null
+++ b/ring-android/app/src/main/java/cx/ring/service/OutgoingCallHandler.kt
@@ -0,0 +1,58 @@
+/*
+ *  Copyright (C) 2004-2021 Savoir-faire Linux Inc.
+ *
+ *  Author: Adrien Béraud <adrien.beraud@savoirfairelinux.com>
+ *
+ *  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, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+package cx.ring.service
+
+import android.content.BroadcastReceiver
+import android.content.Context
+import android.content.Intent
+import android.net.Uri
+import android.preference.PreferenceManager
+import cx.ring.R
+import cx.ring.client.CallActivity
+
+class OutgoingCallHandler : BroadcastReceiver() {
+    override fun onReceive(context: Context, intent: Intent) {
+        if (Intent.ACTION_NEW_OUTGOING_CALL != intent.action) return
+        var phoneNumber = resultData
+        if (phoneNumber == null) phoneNumber = intent.getStringExtra(Intent.EXTRA_PHONE_NUMBER)
+        val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context)
+        val systemDialer = sharedPreferences.getBoolean(context.getString(R.string.pref_systemDialer_key), false)
+        if (systemDialer) {
+            val systemDialerSip = sharedPreferences.getBoolean(KEY_CACHE_HAVE_SIPACCOUNT, false)
+            val systemDialerRing = sharedPreferences.getBoolean(KEY_CACHE_HAVE_RINGACCOUNT, false)
+            val uri = net.jami.model.Uri.fromString(phoneNumber!!)
+            val isRingId = uri.isHexId
+            if (!isRingId && systemDialerSip || isRingId && systemDialerRing || uri.isSingleIp) {
+                val i = Intent(Intent.ACTION_CALL)
+                    .setClass(context, CallActivity::class.java)
+                    .setData(Uri.parse(phoneNumber))
+                    .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
+                context.startActivity(i)
+                resultData = null
+            }
+        }
+    }
+
+    companion object {
+        const val KEY_CACHE_HAVE_RINGACCOUNT = "cache_haveRingAccount"
+        const val KEY_CACHE_HAVE_SIPACCOUNT = "cache_haveSipAccount"
+        private val TAG = OutgoingCallHandler::class.java.simpleName
+    }
+}
\ No newline at end of file
diff --git a/ring-android/app/src/main/java/cx/ring/services/ContactServiceImpl.kt b/ring-android/app/src/main/java/cx/ring/services/ContactServiceImpl.kt
index d567f54b6..1596ad3d6 100644
--- a/ring-android/app/src/main/java/cx/ring/services/ContactServiceImpl.kt
+++ b/ring-android/app/src/main/java/cx/ring/services/ContactServiceImpl.kt
@@ -38,6 +38,7 @@ import io.reactivex.rxjava3.schedulers.Schedulers
 import net.jami.model.Contact
 import net.jami.model.Conversation
 import net.jami.model.Phone
+import net.jami.model.Profile
 import net.jami.services.AccountService
 import net.jami.services.ContactService
 import net.jami.services.DeviceRuntimeService
@@ -183,12 +184,7 @@ class ContactServiceImpl(val mContext: Context, preferenceService: PreferencesSe
                 val indexPhoto = result.getColumnIndex(ContactsContract.Data.PHOTO_ID)
                 val indexStared = result.getColumnIndex(ContactsContract.Contacts.STARRED)
                 val contactId = result.getLong(indexId)
-                Log.d(
-                    TAG,
-                    "Contact name: " + result.getString(indexName) + " id:" + contactId + " key:" + result.getString(
-                        indexKey
-                    )
-                )
+                Log.d(TAG, "Contact name: " + result.getString(indexName) + " id:" + contactId + " key:" + result.getString(indexKey))
                 contact = Contact(net.jami.model.Uri.fromString(contentUri.toString()))
                 contact.setSystemContactInfo(
                     contactId,
@@ -208,7 +204,7 @@ class ContactServiceImpl(val mContext: Context, preferenceService: PreferencesSe
         if (contact == null) {
             Log.d(TAG, "findContactByIdFromSystem: findById $id can't find contact.")
         }
-        return contact!!
+        return contact
     }
 
     private fun fillContactDetails(contact: Contact) {
@@ -219,12 +215,9 @@ class ContactServiceImpl(val mContext: Context, preferenceService: PreferencesSe
                 CONTACTS_PHONES_PROJECTION, ID_SELECTION, arrayOf(contact.id.toString()), null
             )
             if (cursorPhones != null) {
-                val indexNumber =
-                    cursorPhones.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER)
-                val indexType =
-                    cursorPhones.getColumnIndex(ContactsContract.CommonDataKinds.Phone.TYPE)
-                val indexLabel =
-                    cursorPhones.getColumnIndex(ContactsContract.CommonDataKinds.Phone.LABEL)
+                val indexNumber = cursorPhones.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER)
+                val indexType = cursorPhones.getColumnIndex(ContactsContract.CommonDataKinds.Phone.TYPE)
+                val indexLabel = cursorPhones.getColumnIndex(ContactsContract.CommonDataKinds.Phone.LABEL)
                 while (cursorPhones.moveToNext()) {
                     contact.addNumber(
                         cursorPhones.getString(indexNumber),
@@ -232,19 +225,12 @@ class ContactServiceImpl(val mContext: Context, preferenceService: PreferencesSe
                         cursorPhones.getString(indexLabel),
                         Phone.NumberType.TEL
                     )
-                    Log.d(
-                        TAG,
-                        "Phone:" + cursorPhones.getString(
-                            cursorPhones.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER)
-                        )
-                    )
+                    Log.d(TAG, "Phone:" + cursorPhones.getString(cursorPhones.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER)))
                 }
                 cursorPhones.close()
             }
-            val baseUri =
-                ContentUris.withAppendedId(ContactsContract.Contacts.CONTENT_URI, contact.id)
-            val targetUri =
-                Uri.withAppendedPath(baseUri, ContactsContract.Contacts.Data.CONTENT_DIRECTORY)
+            val baseUri = ContentUris.withAppendedId(ContactsContract.Contacts.CONTENT_URI, contact.id)
+            val targetUri = Uri.withAppendedPath(baseUri, ContactsContract.Contacts.Data.CONTENT_DIRECTORY)
             val cursorSip = contentResolver.query(
                 targetUri,
                 CONTACTS_SIP_PROJECTION,
@@ -257,22 +243,15 @@ class ContactServiceImpl(val mContext: Context, preferenceService: PreferencesSe
             )
             if (cursorSip != null) {
                 val indexMime = cursorSip.getColumnIndex(ContactsContract.Data.MIMETYPE)
-                val indexSip =
-                    cursorSip.getColumnIndex(ContactsContract.CommonDataKinds.SipAddress.SIP_ADDRESS)
-                val indexType =
-                    cursorSip.getColumnIndex(ContactsContract.CommonDataKinds.SipAddress.TYPE)
-                val indexLabel =
-                    cursorSip.getColumnIndex(ContactsContract.CommonDataKinds.SipAddress.LABEL)
+                val indexSip = cursorSip.getColumnIndex(ContactsContract.CommonDataKinds.SipAddress.SIP_ADDRESS)
+                val indexType = cursorSip.getColumnIndex(ContactsContract.CommonDataKinds.SipAddress.TYPE)
+                val indexLabel = cursorSip.getColumnIndex(ContactsContract.CommonDataKinds.SipAddress.LABEL)
                 while (cursorSip.moveToNext()) {
                     val contactMime = cursorSip.getString(indexMime)
                     val contactNumber = cursorSip.getString(indexSip)
-                    if (!contactMime.contentEquals(ContactsContract.CommonDataKinds.Im.CONTENT_ITEM_TYPE) || net.jami.model.Uri.fromString(
-                            contactNumber
-                        ).isHexId || "ring".equals(
-                            cursorSip.getString(indexLabel),
-                            ignoreCase = true
-                        )
-                    ) {
+                    if (contactMime != ContactsContract.CommonDataKinds.Im.CONTENT_ITEM_TYPE
+                        || net.jami.model.Uri.fromString(contactNumber).isHexId
+                        || "ring".equals(cursorSip.getString(indexLabel),ignoreCase = true)) {
                         contact.addNumber(
                             contactNumber,
                             cursorSip.getInt(indexType),
@@ -328,27 +307,20 @@ class ContactServiceImpl(val mContext: Context, preferenceService: PreferencesSe
                 fillContactDetails(contact)
             }
             result.close()
-            if (contact == null || contact.phones == null || contact.phones.isEmpty()) {
+            if (contact?.phones == null || contact.phones.isEmpty()) {
                 return null
             }
         } catch (e: Exception) {
-            Log.d(
-                TAG,
-                "findContactBySipNumberFromSystem: Error while searching for contact number=$number",
-                e
-            )
+            Log.d(TAG, "findContactBySipNumberFromSystem: Error while searching for contact number=$number", e)
         }
-        return contact!!
+        return contact
     }
 
     public override fun findContactByNumberFromSystem(number: String): Contact? {
         var contact: Contact? = null
         val contentResolver = mContext.contentResolver
         try {
-            val uri = Uri.withAppendedPath(
-                ContactsContract.PhoneLookup.CONTENT_FILTER_URI,
-                Uri.encode(number)
-            )
+            val uri = Uri.withAppendedPath(ContactsContract.PhoneLookup.CONTENT_FILTER_URI, Uri.encode(number))
             val result = contentResolver.query(uri, PHONELOOKUP_PROJECTION, null, null, null)
             if (result == null) {
                 Log.d(TAG, "findContactByNumberFromSystem: $number can't find contact.")
@@ -367,10 +339,7 @@ class ContactServiceImpl(val mContext: Context, preferenceService: PreferencesSe
                     result.getLong(indexPhoto)
                 )
                 fillContactDetails(contact)
-                Log.d(
-                    TAG,
-                    "findContactByNumberFromSystem: " + number + " found " + contact.displayName
-                )
+                Log.d(TAG, "findContactByNumberFromSystem: " + number + " found " + contact.displayName)
             }
             result.close()
         } catch (e: Exception) {
@@ -386,12 +355,12 @@ class ContactServiceImpl(val mContext: Context, preferenceService: PreferencesSe
 
     override fun loadContactData(contact: Contact, accountId: String): Completable {
         if (!contact.detailsLoaded) {
-            val profile: Single<Tuple<String?, Any?>> =
+            val profile: Single<Profile> =
                 if (contact.isFromSystem) loadSystemContactData(contact)
                 else loadVCardContactData(contact, accountId)
             return profile
-                .doOnSuccess { p: Tuple<String?, Any?> -> contact.setProfile(p.first, p.second) }
-                .doOnError { e: Throwable? -> contact.setProfile(null, null) }
+                .doOnSuccess { p -> contact.setProfile(p) }
+                .doOnError { e: Throwable -> contact.setProfile(null, null) }
                 .ignoreElement()
                 .onErrorComplete()
         }
@@ -399,18 +368,13 @@ class ContactServiceImpl(val mContext: Context, preferenceService: PreferencesSe
     }
 
     override fun saveVCardContactData(contact: Contact, accountId: String, vcard: VCard) {
-        if (vcard != null) {
-            val profileData = VCardServiceImpl.readData(vcard)
-            contact.setProfile(profileData.first, profileData.second)
-            val filename = contact.primaryNumber + ".vcf"
-            VCardUtils.savePeerProfileToDisk(
-                vcard, accountId, filename, mContext.filesDir
-            )
-            AvatarFactory.clearCache()
-        }
+        contact.setProfile(VCardServiceImpl.readData(vcard))
+        val filename = contact.primaryNumber + ".vcf"
+        VCardUtils.savePeerProfileToDisk(vcard, accountId, filename, mContext.filesDir)
+        AvatarFactory.clearCache()
     }
 
-    override fun saveVCardContact(accountId: String, uri: String, displayName: String, picture: String): Single<VCard> {
+    override fun saveVCardContact(accountId: String, uri: String?, displayName: String?, picture: String?): Single<VCard> {
         return Single.fromCallable {
             val vcard = VCardUtils.writeData(uri, displayName, Base64.decode(picture, Base64.DEFAULT))
             val filename = "$uri.vcf"
@@ -419,23 +383,20 @@ class ContactServiceImpl(val mContext: Context, preferenceService: PreferencesSe
         }
     }
 
-    private fun loadVCardContactData(contact: Contact, accountId: String): Single<Tuple<String?, Any?>> {
+    private fun loadVCardContactData(contact: Contact, accountId: String): Single<Profile> {
         val id = contact.primaryNumber
         return Single.fromCallable<VCard> { VCardUtils.loadPeerProfileFromDisk(mContext.filesDir, "$id.vcf", accountId) }
             .map { vcard: VCard -> VCardServiceImpl.readData(vcard) }
             .subscribeOn(Schedulers.computation())
     }
 
-    private fun loadSystemContactData(contact: Contact): Single<Tuple<String?, Any?>> {
+    private fun loadSystemContactData(contact: Contact): Single<Profile> {
         val contactName = contact.displayName
         val photoURI = ContentUris.withAppendedId(ContactsContract.Contacts.CONTENT_URI, contact.id)
         return AndroidFileUtils
-            .loadBitmap(
-                mContext,
-                Uri.withAppendedPath(photoURI, ContactsContract.Contacts.Photo.DISPLAY_PHOTO)
-            )
-            .map { bitmap: Bitmap? -> Tuple<String?, Any?>(contactName, bitmap) }
-            .onErrorReturn { e: Throwable? -> Tuple(contactName, null) }
+            .loadBitmap(mContext, Uri.withAppendedPath(photoURI, ContactsContract.Contacts.Photo.DISPLAY_PHOTO))
+            .map { bitmap: Bitmap -> Profile(contactName, bitmap) }
+            .onErrorReturn { Profile(contactName, null) }
             .subscribeOn(Schedulers.io())
     }
 
diff --git a/ring-android/app/src/main/java/cx/ring/services/HardwareServiceImpl.kt b/ring-android/app/src/main/java/cx/ring/services/HardwareServiceImpl.kt
index fe7abca95..3e618299b 100644
--- a/ring-android/app/src/main/java/cx/ring/services/HardwareServiceImpl.kt
+++ b/ring-android/app/src/main/java/cx/ring/services/HardwareServiceImpl.kt
@@ -30,6 +30,7 @@ import android.media.AudioManager.OnAudioFocusChangeListener
 import android.media.MediaRecorder
 import android.media.projection.MediaProjection
 import android.os.Build
+import android.util.Log
 import android.view.SurfaceHolder
 import android.view.TextureView
 import android.view.WindowManager
@@ -50,7 +51,6 @@ import net.jami.model.Call.CallStatus
 import net.jami.model.Conference
 import net.jami.services.HardwareService
 import net.jami.services.PreferencesService
-import net.jami.utils.Log
 import net.jami.utils.Tuple
 import java.io.File
 import java.lang.ref.WeakReference
@@ -63,7 +63,7 @@ class HardwareServiceImpl(
     preferenceService: PreferencesService,
     uiScheduler: Scheduler
 ) : HardwareService(executor, preferenceService, uiScheduler), OnAudioFocusChangeListener, BluetoothChangeListener {
-    private val videoInputs: MutableMap<String?, Shm> = HashMap()
+    private val videoInputs: MutableMap<String, Shm> = HashMap()
     private val cameraService = CameraService(mContext)
     private val mRinger = Ringer(mContext)
     private val mAudioManager: AudioManager = mContext.getSystemService(Context.AUDIO_SERVICE) as AudioManager
@@ -78,12 +78,13 @@ class HardwareServiceImpl(
     private var mIsChoosePlugin = false
     private var mMediaHandlerId: String? = null
     private var mPluginCallId: String? = null
+
     override fun initVideo(): Completable {
         Log.i(TAG, "initVideo()")
         return cameraService.init()
     }
 
-    override val maxResolutions: Observable<Tuple<Int, Int>>
+    override val maxResolutions: Observable<Tuple<Int?, Int?>>
         get() = cameraService.maxResolutions
     override val isVideoAvailable: Boolean
         get() = mContext.packageManager.hasSystemFeature(PackageManager.FEATURE_CAMERA_ANY) || cameraService.hasCamera()
@@ -119,37 +120,29 @@ class HardwareServiceImpl(
         get() = mAudioManager.isSpeakerphoneOn
 
     private val RINGTONE_REQUEST = AudioFocusRequestCompat.Builder(AudioManagerCompat.AUDIOFOCUS_GAIN_TRANSIENT)
-        .setAudioAttributes(
-            AudioAttributesCompat.Builder()
-                .setContentType(AudioAttributesCompat.CONTENT_TYPE_MUSIC)
-                .setUsage(AudioAttributesCompat.USAGE_NOTIFICATION_RINGTONE)
-                .setLegacyStreamType(AudioManager.STREAM_RING)
-                .build()
-        )
+        .setAudioAttributes(AudioAttributesCompat.Builder()
+            .setContentType(AudioAttributesCompat.CONTENT_TYPE_MUSIC)
+            .setUsage(AudioAttributesCompat.USAGE_NOTIFICATION_RINGTONE)
+            .setLegacyStreamType(AudioManager.STREAM_RING)
+            .build())
         .setOnAudioFocusChangeListener(this)
         .build()
     private val CALL_REQUEST = AudioFocusRequestCompat.Builder(AudioManagerCompat.AUDIOFOCUS_GAIN_TRANSIENT)
-        .setAudioAttributes(
-            AudioAttributesCompat.Builder()
-                .setContentType(AudioAttributesCompat.CONTENT_TYPE_SPEECH)
-                .setUsage(AudioAttributesCompat.USAGE_VOICE_COMMUNICATION)
-                .setLegacyStreamType(AudioManager.STREAM_VOICE_CALL)
-                .build()
-        )
+        .setAudioAttributes(AudioAttributesCompat.Builder()
+            .setContentType(AudioAttributesCompat.CONTENT_TYPE_SPEECH)
+            .setUsage(AudioAttributesCompat.USAGE_VOICE_COMMUNICATION)
+            .setLegacyStreamType(AudioManager.STREAM_VOICE_CALL)
+            .build())
         .setOnAudioFocusChangeListener(this)
         .build()
 
     private fun getFocus(request: AudioFocusRequestCompat?) {
         if (currentFocus === request) return
-        if (currentFocus != null) {
-            AudioManagerCompat.abandonAudioFocusRequest(mAudioManager, currentFocus!!)
+        currentFocus?.let { focus ->
+            AudioManagerCompat.abandonAudioFocusRequest(mAudioManager, focus)
             currentFocus = null
         }
-        if (request != null && AudioManagerCompat.requestAudioFocus(
-                mAudioManager,
-                request
-            ) == AudioManager.AUDIOFOCUS_REQUEST_GRANTED
-        ) {
+        if (request != null && AudioManagerCompat.requestAudioFocus(mAudioManager, request) == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {
             currentFocus = request
         }
     }
@@ -316,13 +309,11 @@ class HardwareServiceImpl(
     @Synchronized
     override fun onBluetoothStateChanged(status: Int) {
         Log.d(TAG, "bluetoothStateChanged to: $status")
-        val event = BluetoothEvent()
+        val event = BluetoothEvent(status == BluetoothHeadset.STATE_AUDIO_CONNECTED)
         if (status == BluetoothHeadset.STATE_AUDIO_CONNECTED) {
             Log.d(TAG, "BluetoothHeadset Connected")
-            event.connected = true
         } else if (status == BluetoothHeadset.STATE_AUDIO_DISCONNECTED) {
             Log.d(TAG, "BluetoothHeadset Disconnected")
-            event.connected = false
             if (mShouldSpeakerphone) routeToSpeaker()
         }
         bluetoothEvents.onNext(event)
@@ -330,31 +321,24 @@ class HardwareServiceImpl(
 
     override fun decodingStarted(id: String, shmPath: String, width: Int, height: Int, isMixer: Boolean) {
         Log.i(TAG, "decodingStarted() " + id + " " + width + "x" + height)
-        val shm = Shm()
-        shm.id = id
-        shm.w = width
-        shm.h = height
+        val shm = Shm(id, width, height)
         videoInputs[id] = shm
-        val weakSurfaceHolder = videoSurfaces[id]
-        if (weakSurfaceHolder != null) {
-            val holder = weakSurfaceHolder.get()
-            if (holder != null) {
-                shm.window = startVideo(id, holder.surface, width, height)
-                if (shm.window == 0L) {
-                    Log.i(TAG, "DJamiService.decodingStarted() no window !")
-                    val event = VideoEvent()
-                    event.start = true
-                    event.callId = shm.id
-                    videoEvents.onNext(event)
-                    return
-                }
+        videoSurfaces[id]?.get()?.let { holder ->
+            shm.window = startVideo(id, holder.surface, width, height)
+            if (shm.window == 0L) {
+                Log.i(TAG, "DJamiService.decodingStarted() no window !")
                 val event = VideoEvent()
+                event.start = true
                 event.callId = shm.id
-                event.started = true
-                event.w = shm.w
-                event.h = shm.h
                 videoEvents.onNext(event)
+                return
             }
+            val event = VideoEvent()
+            event.callId = shm.id
+            event.started = true
+            event.w = shm.w
+            event.h = shm.h
+            videoEvents.onNext(event)
         }
     }
 
@@ -529,7 +513,7 @@ class HardwareServiceImpl(
         cameraService.requestKeyFrame()
     }
 
-    override fun setBitrate(device: String?, bitrate: Int) {
+    override fun setBitrate(device: String, bitrate: Int) {
         cameraService.setBitrate(bitrate)
     }
 
@@ -546,7 +530,7 @@ class HardwareServiceImpl(
         mIsCapturing = false
     }
 
-    override fun addVideoSurface(id: String?, holder: Any?) {
+    override fun addVideoSurface(id: String, holder: Any) {
         if (holder !is SurfaceHolder) {
             return
         }
@@ -572,7 +556,7 @@ class HardwareServiceImpl(
         videoEvents.onNext(event)
     }
 
-    override fun updateVideoSurfaceId(currentId: String?, newId: String?) {
+    override fun updateVideoSurfaceId(currentId: String, newId: String) {
         Log.w(TAG, "updateVideoSurfaceId $currentId $newId")
         val surfaceHolder = videoSurfaces[currentId] ?: return
         val surface = surfaceHolder.get()
@@ -589,7 +573,7 @@ class HardwareServiceImpl(
         surface?.let { addVideoSurface(newId, it) }
     }
 
-    override fun addPreviewVideoSurface(holder: Any?, conference: Conference?) {
+    override fun addPreviewVideoSurface(holder: Any, conference: Conference?) {
         if (holder !is TextureView)
             return
         Log.w(TAG, "addPreviewVideoSurface " + holder.hashCode() + " mCapturingId " + mCapturingId)
@@ -601,7 +585,7 @@ class HardwareServiceImpl(
         }
     }
 
-    override fun updatePreviewVideoSurface(conference: Conference?) {
+    override fun updatePreviewVideoSurface(conference: Conference) {
         val old = mCameraPreviewCall.get()
         mCameraPreviewCall = WeakReference(conference)
         if (old !== conference && mIsCapturing) {
@@ -611,7 +595,7 @@ class HardwareServiceImpl(
         }
     }
 
-    override fun removeVideoSurface(id: String?) {
+    override fun removeVideoSurface(id: String) {
         Log.i(TAG, "removeVideoSurface $id")
         videoSurfaces.remove(id)
         val shm = videoInputs[id] ?: return
@@ -634,7 +618,7 @@ class HardwareServiceImpl(
         mCameraPreviewSurface.clear()
     }
 
-    override fun switchInput(id: String?, setDefaultCamera: Boolean) {
+    override fun switchInput(id: String, setDefaultCamera: Boolean) {
         Log.w(TAG, "switchInput $id")
         mCapturingId = cameraService.switchInput(setDefaultCamera)
         switchInput(id, "camera://$mCapturingId")
@@ -670,10 +654,7 @@ class HardwareServiceImpl(
     override val videoDevices: List<String>
         get() = cameraService.cameraIds
 
-    private class Shm {
-        var id: String? = null
-        var w = 0
-        var h = 0
+    private class Shm (val id: String, val w: Int, val h: Int) {
         var window: Long = 0
     }
 
@@ -688,8 +669,8 @@ class HardwareServiceImpl(
         private val VIDEO_SIZE_FULL_HD = Point(1920, 1080)
         private val VIDEO_SIZE_ULTRA_HD = Point(3840, 2160)
         private val TAG = HardwareServiceImpl::class.simpleName!!
-        private var mCameraPreviewSurface = WeakReference<TextureView?>(null)
-        private var mCameraPreviewCall = WeakReference<Conference?>(null)
-        private val videoSurfaces = Collections.synchronizedMap(HashMap<String?, WeakReference<SurfaceHolder>>())
+        private var mCameraPreviewSurface = WeakReference<TextureView>(null)
+        private var mCameraPreviewCall = WeakReference<Conference>(null)
+        private val videoSurfaces = Collections.synchronizedMap(HashMap<String, WeakReference<SurfaceHolder>>())
     }
 }
\ No newline at end of file
diff --git a/ring-android/app/src/main/java/cx/ring/services/LogServiceImpl.java b/ring-android/app/src/main/java/cx/ring/services/LogServiceImpl.kt
similarity index 52%
rename from ring-android/app/src/main/java/cx/ring/services/LogServiceImpl.java
rename to ring-android/app/src/main/java/cx/ring/services/LogServiceImpl.kt
index 169e6f490..5eb1858ab 100644
--- a/ring-android/app/src/main/java/cx/ring/services/LogServiceImpl.java
+++ b/ring-android/app/src/main/java/cx/ring/services/LogServiceImpl.kt
@@ -17,43 +17,41 @@
  *  along with this program; if not, write to the Free Software
  *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
-package cx.ring.services;
+package cx.ring.services
 
-import android.util.Log;
+import android.util.Log
+import net.jami.services.LogService
 
-import net.jami.services.LogService;
-
-public class LogServiceImpl implements LogService {
-
-    public void e(String tag, String message) {
-        Log.e(tag, message);
+class LogServiceImpl : LogService {
+    override fun e(tag: String, message: String) {
+        Log.e(tag, message)
     }
 
-    public void d(String tag, String message) {
-        Log.d(tag, message);
+    override fun d(tag: String, message: String) {
+        Log.d(tag, message)
     }
 
-    public void w(String tag, String message) {
-        Log.w(tag, message);
+    override fun w(tag: String, message: String) {
+        Log.w(tag, message)
     }
 
-    public void i(String tag, String message) {
-        Log.i(tag, message);
+    override fun i(tag: String, message: String) {
+        Log.i(tag, message)
     }
 
-    public void e(String tag, String message, Throwable e) {
-        Log.e(tag, message, e);
+    override fun e(tag: String, message: String, e: Throwable) {
+        Log.e(tag, message, e)
     }
 
-    public void d(String tag, String message, Throwable e) {
-        Log.d(tag, message, e);
+    override fun d(tag: String, message: String, e: Throwable) {
+        Log.d(tag, message, e)
     }
 
-    public void w(String tag, String message, Throwable e) {
-        Log.w(tag, message, e);
+    override fun w(tag: String, message: String, e: Throwable) {
+        Log.w(tag, message, e)
     }
 
-    public void i(String tag, String message, Throwable e) {
-        Log.i(tag, message, e);
+    override fun i(tag: String, message: String, e: Throwable) {
+        Log.i(tag, message, e)
     }
-}
+}
\ No newline at end of file
diff --git a/ring-android/app/src/main/java/cx/ring/services/NotificationServiceImpl.kt b/ring-android/app/src/main/java/cx/ring/services/NotificationServiceImpl.kt
index 670963168..f0123c874 100644
--- a/ring-android/app/src/main/java/cx/ring/services/NotificationServiceImpl.kt
+++ b/ring-android/app/src/main/java/cx/ring/services/NotificationServiceImpl.kt
@@ -509,7 +509,7 @@ class NotificationServiceImpl(
             val myPic = account?.let { getContactPicture(it) }
             val userPerson = Person.Builder()
                 .setKey(accountId)
-                .setName(if (profile == null || TextUtils.isEmpty(profile.first)) "You" else profile.first)
+                .setName(if (profile == null || TextUtils.isEmpty(profile.displayName)) "You" else profile.displayName)
                 .setIcon(if (myPic == null) null else IconCompat.createWithBitmap(myPic))
                 .build()
             val history = NotificationCompat.MessagingStyle(userPerson)
@@ -831,23 +831,22 @@ class NotificationServiceImpl(
         notificationManager.notify(notificationId, messageNotificationBuilder.build())
     }
 
-    override fun getServiceNotification(): Any {
-        val intentHome = Intent(Intent.ACTION_VIEW)
-            .setClass(mContext, HomeActivity::class.java)
-            .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
-        val pendIntent = PendingIntent.getActivity(mContext, 0, intentHome, PendingIntent.FLAG_UPDATE_CURRENT)
-        val messageNotificationBuilder = NotificationCompat.Builder(mContext, NOTIF_CHANNEL_SERVICE)
-        messageNotificationBuilder
-            .setContentTitle(mContext.getText(R.string.app_name))
-            .setContentText(mContext.getText(R.string.notif_background_service))
-            .setSmallIcon(R.drawable.ic_ring_logo_white)
-            .setContentIntent(pendIntent)
-            .setVisibility(NotificationCompat.VISIBILITY_SECRET)
-            .setPriority(NotificationCompat.PRIORITY_MIN)
-            .setOngoing(true)
-            .setCategory(NotificationCompat.CATEGORY_SERVICE)
-        return messageNotificationBuilder.build()
-    }
+    override val serviceNotification: Any
+        get() {
+            val intentHome = Intent(Intent.ACTION_VIEW)
+                .setClass(mContext, HomeActivity::class.java)
+                .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
+            return NotificationCompat.Builder(mContext, NOTIF_CHANNEL_SERVICE)
+                .setContentTitle(mContext.getText(R.string.app_name))
+                .setContentText(mContext.getText(R.string.notif_background_service))
+                .setSmallIcon(R.drawable.ic_ring_logo_white)
+                .setContentIntent(PendingIntent.getActivity(mContext, 0, intentHome, PendingIntent.FLAG_UPDATE_CURRENT))
+                .setVisibility(NotificationCompat.VISIBILITY_SECRET)
+                .setPriority(NotificationCompat.PRIORITY_MIN)
+                .setOngoing(true)
+                .setCategory(NotificationCompat.CATEGORY_SERVICE)
+                .build()
+        }
 
     override fun cancelTextNotification(accountId: String, contact: net.jami.model.Uri) {
         val notificationId = getTextNotificationId(accountId, contact)
diff --git a/ring-android/app/src/main/java/cx/ring/services/SharedPreferencesServiceImpl.kt b/ring-android/app/src/main/java/cx/ring/services/SharedPreferencesServiceImpl.kt
index 225893201..1e4e39e41 100644
--- a/ring-android/app/src/main/java/cx/ring/services/SharedPreferencesServiceImpl.kt
+++ b/ring-android/app/src/main/java/cx/ring/services/SharedPreferencesServiceImpl.kt
@@ -21,64 +21,54 @@
 package cx.ring.services
 
 import android.content.Context
-import net.jami.services.PreferencesService
-import java.util.HashMap
 import android.content.SharedPreferences
-import java.util.HashSet
-import cx.ring.utils.NetworkUtils
-import cx.ring.application.JamiApplication
+import android.os.Build
 import android.text.TextUtils
-import cx.ring.utils.DeviceUtils
-import cx.ring.R
 import androidx.appcompat.app.AppCompatDelegate
-import android.os.Build
 import androidx.preference.PreferenceManager
+import cx.ring.R
+import cx.ring.application.JamiApplication
+import cx.ring.utils.DeviceUtils
+import cx.ring.utils.NetworkUtils
 import net.jami.model.Settings
 import net.jami.model.Uri
 import net.jami.services.AccountService
 import net.jami.services.DeviceRuntimeService
+import net.jami.services.PreferencesService
+import java.util.*
 
-class SharedPreferencesServiceImpl(val mContext: Context, accountService: AccountService, deviceService: DeviceRuntimeService) : PreferencesService(accountService, deviceService) {
+class SharedPreferencesServiceImpl(val mContext: Context, accountService: AccountService, deviceService: DeviceRuntimeService)
+    : PreferencesService(accountService, deviceService) {
     private val mNotifiedRequests: MutableMap<String, MutableSet<String>> = HashMap()
 
     override fun saveSettings(settings: Settings) {
-        val appPrefs = preferences
-        val edit = appPrefs.edit()
-        edit.clear()
-        edit.putBoolean(PREF_SYSTEM_CONTACTS, settings.isAllowSystemContacts)
-        edit.putBoolean(PREF_PLACE_CALLS, settings.isAllowPlaceSystemCalls)
-        edit.putBoolean(PREF_ON_STARTUP, settings.isAllowOnStartup)
-        edit.putBoolean(PREF_PUSH_NOTIFICATIONS, settings.isAllowPushNotifications)
-        edit.putBoolean(PREF_PERSISTENT_NOTIFICATION, settings.isAllowPersistentNotification)
-        edit.putBoolean(PREF_SHOW_TYPING, settings.isAllowTypingIndicator)
-        edit.putBoolean(PREF_SHOW_READ, settings.isAllowReadIndicator)
-        edit.putBoolean(PREF_BLOCK_RECORD, settings.isRecordingBlocked)
-        edit.putInt(PREF_NOTIFICATION_VISIBILITY, settings.notificationVisibility)
-        edit.apply()
+        preferences.edit()
+            .clear()
+            .putBoolean(PREF_SYSTEM_CONTACTS, settings.isAllowSystemContacts)
+            .putBoolean(PREF_PLACE_CALLS, settings.isAllowPlaceSystemCalls)
+            .putBoolean(PREF_ON_STARTUP, settings.isAllowOnStartup)
+            .putBoolean(PREF_PUSH_NOTIFICATIONS, settings.isAllowPushNotifications)
+            .putBoolean(PREF_PERSISTENT_NOTIFICATION, settings.isAllowPersistentNotification)
+            .putBoolean(PREF_SHOW_TYPING, settings.isAllowTypingIndicator)
+            .putBoolean(PREF_SHOW_READ, settings.isAllowReadIndicator)
+            .putBoolean(PREF_BLOCK_RECORD, settings.isRecordingBlocked)
+            .putInt(PREF_NOTIFICATION_VISIBILITY, settings.notificationVisibility)
+            .apply()
     }
 
     override fun loadSettings(): Settings {
         val appPrefs = preferences
-        val settings = userSettings ?: Settings()
-        settings.isAllowSystemContacts =
-            appPrefs.getBoolean(PREF_SYSTEM_CONTACTS, false)
-        settings.isAllowPlaceSystemCalls =
-            appPrefs.getBoolean(PREF_PLACE_CALLS, false)
-        settings.setAllowRingOnStartup(appPrefs.getBoolean(PREF_ON_STARTUP, true))
-        settings.isAllowPushNotifications =
-            appPrefs.getBoolean(PREF_PUSH_NOTIFICATIONS, false)
-        settings.isAllowPersistentNotification = appPrefs.getBoolean(
-            PREF_PERSISTENT_NOTIFICATION,
-            false
-        )
-        settings.isAllowTypingIndicator =
-            appPrefs.getBoolean(PREF_SHOW_TYPING, true)
-        settings.isAllowReadIndicator =
-            appPrefs.getBoolean(PREF_SHOW_READ, true)
-        settings.setBlockRecordIndicator(appPrefs.getBoolean(PREF_BLOCK_RECORD, false))
-        settings.notificationVisibility =
-            appPrefs.getInt(PREF_NOTIFICATION_VISIBILITY, 0)
-        return settings
+        return userSettings ?: Settings().apply {
+            isAllowSystemContacts = appPrefs.getBoolean(PREF_SYSTEM_CONTACTS, false)
+            isAllowPlaceSystemCalls = appPrefs.getBoolean(PREF_PLACE_CALLS, false)
+            setAllowRingOnStartup(appPrefs.getBoolean(PREF_ON_STARTUP, true))
+            isAllowPushNotifications = appPrefs.getBoolean(PREF_PUSH_NOTIFICATIONS, false)
+            isAllowPersistentNotification = appPrefs.getBoolean(PREF_PERSISTENT_NOTIFICATION, false)
+            isAllowTypingIndicator = appPrefs.getBoolean(PREF_SHOW_TYPING, true)
+            isAllowReadIndicator = appPrefs.getBoolean(PREF_SHOW_READ, true)
+            setBlockRecordIndicator(appPrefs.getBoolean(PREF_BLOCK_RECORD, false))
+            notificationVisibility = appPrefs.getInt(PREF_NOTIFICATION_VISIBILITY, 0)
+        }
     }
 
     private fun saveRequests(accountId: String, requests: Set<String>) {
@@ -114,40 +104,27 @@ class SharedPreferencesServiceImpl(val mContext: Context, accountService: Accoun
         return NetworkUtils.isConnectivityAllowed(mContext)
     }
 
-    override fun isPushAllowed(): Boolean {
-        val token = JamiApplication.instance?.pushToken
-        return settings.isAllowPushNotifications && !TextUtils.isEmpty(token) /*&& NetworkUtils.isPushAllowed(mContext, getSettings().isAllowMobileData())*/
-    }
+    override val isPushAllowed: Boolean
+        get() {
+            val token = JamiApplication.instance?.pushToken
+            return settings.isAllowPushNotifications && !TextUtils.isEmpty(token) /*&& NetworkUtils.isPushAllowed(mContext, getSettings().isAllowMobileData())*/
+        }
 
-    override fun getResolution(): Int {
-        return videoPreferences.getString(
-            PREF_RESOLUTION,
-            if (DeviceUtils.isTv(mContext)) mContext.getString(R.string.video_resolution_default_tv)
-            else mContext.getString(R.string.video_resolution_default)
-        )!!.toInt()
-    }
+    override val resolution: Int
+        get() = videoPreferences.getString(PREF_RESOLUTION, if (DeviceUtils.isTv(mContext)) mContext.getString(R.string.video_resolution_default_tv) else mContext.getString(R.string.video_resolution_default))!!.toInt()
 
-    override fun getBitrate(): Int {
-        return videoPreferences.getString(
-            PREF_BITRATE,
-            mContext.getString(R.string.video_bitrate_default)
-        )!!.toInt()
-    }
+    override val bitrate: Int
+        get() = videoPreferences.getString(PREF_BITRATE, mContext.getString(R.string.video_bitrate_default))!!.toInt()
 
-    override fun isHardwareAccelerationEnabled(): Boolean {
-        return videoPreferences.getBoolean(PREF_HW_ENCODING, true)
-    }
-
-    override fun setDarkMode(enabled: Boolean) {
-        val edit = themePreferences.edit()
-        edit.putBoolean(PREF_DARK_MODE, enabled)
-            .apply()
-        applyDarkMode(enabled)
-    }
+    override val isHardwareAccelerationEnabled: Boolean
+        get() = videoPreferences.getBoolean(PREF_HW_ENCODING, true)
 
-    override fun getDarkMode(): Boolean {
-        return themePreferences.getBoolean(PREF_DARK_MODE, false)
-    }
+    override var darkMode: Boolean
+        get() = themePreferences.getBoolean(PREF_DARK_MODE, false)
+        set(enabled) {
+            themePreferences.edit().putBoolean(PREF_DARK_MODE, enabled).apply()
+            applyDarkMode(enabled)
+        }
 
     override fun loadDarkMode() {
         applyDarkMode(darkMode)
@@ -190,15 +167,8 @@ class SharedPreferencesServiceImpl(val mContext: Context, accountService: Accoun
         private const val PREF_ACCEPT_IN_MAX_SIZE = "acceptIncomingFilesMaxSize"
         const val PREF_PLUGINS = "plugins"
 
-        @JvmStatic fun getConversationPreferences(
-            context: Context,
-            accountId: String,
-            conversationUri: Uri
-        ): SharedPreferences {
-            return context.getSharedPreferences(
-                accountId + "_" + conversationUri.uri,
-                Context.MODE_PRIVATE
-            )
+        fun getConversationPreferences(context: Context, accountId: String, conversationUri: Uri): SharedPreferences {
+            return context.getSharedPreferences(accountId + "_" + conversationUri.uri, Context.MODE_PRIVATE)
         }
     }
 }
\ No newline at end of file
diff --git a/ring-android/app/src/main/java/cx/ring/services/VCardServiceImpl.kt b/ring-android/app/src/main/java/cx/ring/services/VCardServiceImpl.kt
index 9ee35c4ff..353a0a980 100644
--- a/ring-android/app/src/main/java/cx/ring/services/VCardServiceImpl.kt
+++ b/ring-android/app/src/main/java/cx/ring/services/VCardServiceImpl.kt
@@ -35,11 +35,11 @@ import io.reactivex.rxjava3.core.Observable
 import io.reactivex.rxjava3.core.Single
 import io.reactivex.rxjava3.schedulers.Schedulers
 import net.jami.model.Account
-import net.jami.utils.Tuple
+import net.jami.model.Profile
 import java.io.File
 
 class VCardServiceImpl(private val mContext: Context) : VCardService() {
-    override fun loadProfile(account: Account): Observable<Tuple<String?, Any?>> {
+    override fun loadProfile(account: Account): Observable<Profile> {
         return loadProfile(mContext, account)
     }
 
@@ -61,26 +61,31 @@ class VCardServiceImpl(private val mContext: Context) : VCardService() {
             }
     }
 
-    override fun saveVCardProfile(accountId: String, uri: String, displayName: String, picture: String): Single<VCard> {
+    override fun saveVCardProfile(accountId: String, uri: String?, displayName: String?, picture: String?): Single<VCard> {
         return Single.fromCallable { VCardUtils.writeData(uri, displayName, Base64.decode(picture, Base64.DEFAULT)) }
             .flatMap { vcard: VCard -> VCardUtils.saveLocalProfileToDisk(vcard, accountId, mContext.filesDir) }
     }
 
-    override fun loadVCardProfile(vcard: VCard): Single<Tuple<String?, Any?>> {
+    override fun loadVCardProfile(vcard: VCard): Single<Profile> {
         return Single.fromCallable { readData(vcard) }
     }
 
-    override fun peerProfileReceived(accountId: String, peerId: String, vcardFile: File): Single<Tuple<String?, Any?>> {
-        return VCardUtils.peerProfileReceived(mContext.filesDir, accountId, peerId, vcardFile)
+    override fun peerProfileReceived(accountId: String, peerId: String, vcard: File): Single<Profile> {
+        return VCardUtils.peerProfileReceived(mContext.filesDir, accountId, peerId, vcard)
+            .map { vc -> readData(vc) }
+    }
+
+    override fun accountProfileReceived(accountId: String, vcardFile: File): Single<Profile> {
+        return VCardUtils.accountProfileReceived(mContext.filesDir, accountId, vcardFile)
             .map { vcard -> readData(vcard) }
     }
 
-    override fun base64ToBitmap(base64: String): Any? {
+    override fun base64ToBitmap(base64: String?): Any? {
         return BitmapUtils.base64ToBitmap(base64)
     }
 
     companion object {
-        fun loadProfile(context: Context, account: Account): Observable<Tuple<String?, Any?>> {
+        fun loadProfile(context: Context, account: Account): Observable<Profile> {
             synchronized(account) {
                 var ret = account.loadedProfile
                 if (ret == null) {
@@ -94,12 +99,12 @@ class VCardServiceImpl(private val mContext: Context) : VCardService() {
             }
         }
 
-        fun readData(vcard: VCard?): Tuple<String?, Any?> {
+        fun readData(vcard: VCard?): Profile {
             return readData(VCardUtils.readData(vcard))
         }
 
-        fun readData(profile: Tuple<String?, ByteArray?>): Tuple<String?, Any?> {
-            return Tuple(profile.first, BitmapUtils.bytesToBitmap(profile.second))
+        private fun readData(profile: Pair<String?, ByteArray?>): Profile {
+            return Profile(profile.first, BitmapUtils.bytesToBitmap(profile.second))
         }
     }
 }
\ No newline at end of file
diff --git a/ring-android/app/src/main/java/cx/ring/settings/AccountFragment.java b/ring-android/app/src/main/java/cx/ring/settings/AccountFragment.java
deleted file mode 100644
index 100c966ef..000000000
--- a/ring-android/app/src/main/java/cx/ring/settings/AccountFragment.java
+++ /dev/null
@@ -1,137 +0,0 @@
-/*
- *  Copyright (C) 2004-2019 Savoir-faire Linux Inc.
- *
- *  Author:     AmirHossein Naghshzan <amirhossein.naghshzan@savoirfairelinux.com>
- *
- *  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 cx.ring.settings;
-
-import android.app.Activity;
-import android.os.Bundle;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.ViewTreeObserver;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.appcompat.app.AlertDialog;
-import androidx.fragment.app.Fragment;
-
-import com.google.android.material.dialog.MaterialAlertDialogBuilder;
-
-import javax.inject.Inject;
-
-import cx.ring.R;
-import cx.ring.account.AccountEditionFragment;
-import cx.ring.account.JamiAccountSummaryFragment;
-import cx.ring.application.JamiApplication;
-import cx.ring.client.HomeActivity;
-import cx.ring.databinding.FragAccountBinding;
-import dagger.hilt.android.AndroidEntryPoint;
-import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
-import io.reactivex.rxjava3.disposables.CompositeDisposable;
-
-import net.jami.services.AccountService;
-
-@AndroidEntryPoint
-public class AccountFragment extends Fragment implements ViewTreeObserver.OnScrollChangedListener {
-    public static final String TAG = AccountFragment.class.getSimpleName();
-    private static final int SCROLL_DIRECTION_UP = -1;
-
-    public static AccountFragment newInstance(@NonNull String accountId) {
-        Bundle bundle = new Bundle();
-        bundle.putString(AccountEditionFragment.ACCOUNT_ID_KEY, accountId);
-        AccountFragment accountFragment = new AccountFragment();
-        accountFragment.setArguments(bundle);
-        return accountFragment;
-    }
-
-    private FragAccountBinding mBinding;
-    private final CompositeDisposable mDisposable = new CompositeDisposable();
-
-    @Inject
-    AccountService mAccountService;
-
-    @Nullable
-    @Override
-    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
-        mBinding = FragAccountBinding.inflate(inflater, container, false);
-        return mBinding.getRoot();
-    }
-
-    @Override
-    public void onDestroyView() {
-        super.onDestroyView();
-        mBinding = null;
-        mDisposable.clear();
-    }
-
-    @Override
-    public void onViewCreated(@NonNull View view, Bundle savedInstanceState) {
-        setHasOptionsMenu(true);
-        mBinding.scrollview.getViewTreeObserver().addOnScrollChangedListener(this);
-        mBinding.settingsChangePassword.setOnClickListener(v -> ((JamiAccountSummaryFragment) getParentFragment()).onPasswordChangeAsked());
-        mBinding.settingsExport.setOnClickListener(v -> ((JamiAccountSummaryFragment) getParentFragment()).onClickExport());
-
-        String accountId = getArguments() != null ? getArguments().getString(AccountEditionFragment.ACCOUNT_ID_KEY) : null;
-        mDisposable.add(mAccountService.getAccountSingle(accountId)
-                .observeOn(AndroidSchedulers.mainThread())
-                .subscribe(account -> {
-                    mBinding.settingsChangePassword.setVisibility(account.hasManager() ? View.GONE : View.VISIBLE);
-                    mBinding.settingsExport.setVisibility(account.hasManager() ? View.GONE : View.VISIBLE);
-                    mBinding.systemChangePasswordTitle.setText(account.hasPassword()? R.string.account_password_change : R.string.account_password_set);
-                    mBinding.settingsDeleteAccount.setOnClickListener(v -> {
-                        AlertDialog deleteDialog = createDeleteDialog(account.getAccountID());
-                        deleteDialog.show();
-                    });
-                    mBinding.settingsBlackList.setOnClickListener(v -> {
-                        JamiAccountSummaryFragment summaryFragment = ((JamiAccountSummaryFragment) getParentFragment());
-                        if (summaryFragment != null) {
-                            summaryFragment.goToBlackList(account.getAccountID());
-                        }
-                    });
-                }, e -> {
-                    JamiAccountSummaryFragment summaryFragment = ((JamiAccountSummaryFragment) getParentFragment());
-                    if (summaryFragment != null) {
-                        summaryFragment.popBackStack();
-                    }
-                }));
-    }
-
-    @Override
-    public void onScrollChanged() {
-        if (mBinding != null) {
-            Activity activity = getActivity();
-            if (activity instanceof HomeActivity)
-                ((HomeActivity) activity).setToolbarElevation(mBinding.scrollview.canScrollVertically(SCROLL_DIRECTION_UP));
-        }
-    }
-
-    @NonNull
-    private AlertDialog createDeleteDialog(String accountId) {
-        AlertDialog alertDialog = new MaterialAlertDialogBuilder(requireContext())
-                .setMessage(R.string.account_delete_dialog_message)
-                .setTitle(R.string.account_delete_dialog_title)
-                .setPositiveButton(R.string.menu_delete, (dialog, whichButton) -> mAccountService.removeAccount(accountId))
-                .setNegativeButton(android.R.string.cancel, null)
-                .create();
-        Activity activity = getActivity();
-        if (activity != null)
-            alertDialog.setOwnerActivity(getActivity());
-        return alertDialog;
-    }
-
-}
diff --git a/ring-android/app/src/main/java/cx/ring/settings/AccountFragment.kt b/ring-android/app/src/main/java/cx/ring/settings/AccountFragment.kt
new file mode 100644
index 000000000..9c404e2c7
--- /dev/null
+++ b/ring-android/app/src/main/java/cx/ring/settings/AccountFragment.kt
@@ -0,0 +1,120 @@
+/*
+ *  Copyright (C) 2004-2019 Savoir-faire Linux Inc.
+ *
+ *  Author:     AmirHossein Naghshzan <amirhossein.naghshzan@savoirfairelinux.com>
+ *
+ *  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 cx.ring.settings
+
+import android.app.Activity
+import android.content.DialogInterface
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import android.view.ViewTreeObserver.OnScrollChangedListener
+import androidx.appcompat.app.AlertDialog
+import androidx.fragment.app.Fragment
+import com.google.android.material.dialog.MaterialAlertDialogBuilder
+import cx.ring.R
+import cx.ring.account.AccountEditionFragment
+import cx.ring.account.JamiAccountSummaryFragment
+import cx.ring.client.HomeActivity
+import cx.ring.databinding.FragAccountBinding
+import dagger.hilt.android.AndroidEntryPoint
+import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
+import io.reactivex.rxjava3.disposables.CompositeDisposable
+import net.jami.model.Account
+import net.jami.services.AccountService
+import javax.inject.Inject
+
+@AndroidEntryPoint
+class AccountFragment : Fragment(), OnScrollChangedListener {
+    private var mBinding: FragAccountBinding? = null
+    private val mDisposable = CompositeDisposable()
+
+    @Inject
+    lateinit var mAccountService: AccountService
+
+    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
+        return FragAccountBinding.inflate(inflater, container, false).apply {
+            scrollview.viewTreeObserver.addOnScrollChangedListener(this@AccountFragment)
+            settingsChangePassword.setOnClickListener { (parentFragment as JamiAccountSummaryFragment).onPasswordChangeAsked() }
+            settingsExport.setOnClickListener { (parentFragment as JamiAccountSummaryFragment).onClickExport() }
+            mBinding = this
+        }.root
+    }
+
+    override fun onDestroyView() {
+        super.onDestroyView()
+        mBinding = null
+        mDisposable.clear()
+    }
+
+    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+        setHasOptionsMenu(true)
+        val accountId = requireArguments().getString(AccountEditionFragment.ACCOUNT_ID_KEY)!!
+        mDisposable.add(mAccountService.getAccountSingle(accountId)
+            .observeOn(AndroidSchedulers.mainThread())
+            .subscribe({ account: Account ->
+                mBinding?.let { binding ->
+                    binding.settingsChangePassword.visibility = if (account.hasManager()) View.GONE else View.VISIBLE
+                    binding.settingsExport.visibility = if (account.hasManager()) View.GONE else View.VISIBLE
+                    binding.systemChangePasswordTitle.setText(if (account.hasPassword()) R.string.account_password_change else R.string.account_password_set)
+                    binding.settingsDeleteAccount.setOnClickListener { createDeleteDialog(account.accountID).show() }
+                    binding.settingsBlackList.setOnClickListener {
+                        val summaryFragment = parentFragment as JamiAccountSummaryFragment?
+                        summaryFragment?.goToBlackList(account.accountID)
+                    }
+                }
+            }) {
+                val summaryFragment = parentFragment as JamiAccountSummaryFragment?
+                summaryFragment?.popBackStack()
+            })
+    }
+
+    override fun onScrollChanged() {
+        mBinding?.let { binding ->
+            val activity: Activity? = activity
+            if (activity is HomeActivity)
+                activity.setToolbarElevation(binding.scrollview.canScrollVertically(SCROLL_DIRECTION_UP))
+        }
+    }
+
+    private fun createDeleteDialog(accountId: String): AlertDialog {
+        val alertDialog = MaterialAlertDialogBuilder(requireContext())
+            .setMessage(R.string.account_delete_dialog_message)
+            .setTitle(R.string.account_delete_dialog_title)
+            .setPositiveButton(R.string.menu_delete) { dialog: DialogInterface?, whichButton: Int ->
+                mAccountService.removeAccount(accountId)
+            }
+            .setNegativeButton(android.R.string.cancel, null)
+            .create()
+        activity?.let { activity -> alertDialog.setOwnerActivity(activity) }
+        return alertDialog
+    }
+
+    companion object {
+        val TAG = AccountFragment::class.simpleName!!
+        private const val SCROLL_DIRECTION_UP = -1
+        fun newInstance(accountId: String): AccountFragment {
+            val bundle = Bundle()
+            bundle.putString(AccountEditionFragment.ACCOUNT_ID_KEY, accountId)
+            val accountFragment = AccountFragment()
+            accountFragment.arguments = bundle
+            return accountFragment
+        }
+    }
+}
\ No newline at end of file
diff --git a/ring-android/app/src/main/java/cx/ring/settings/pluginssettings/PathListAdapter.java b/ring-android/app/src/main/java/cx/ring/settings/pluginssettings/PathListAdapter.java
deleted file mode 100644
index 8f724edfb..000000000
--- a/ring-android/app/src/main/java/cx/ring/settings/pluginssettings/PathListAdapter.java
+++ /dev/null
@@ -1,113 +0,0 @@
-/*
- *  Copyright (C) 2004-2021 Savoir-faire Linux Inc.
- *
- *  Author: Aline Gondim Santos <aline.gondimsantos@savoirfairelinux.com>
- *  Author: Adrien Béraud <adrien.beraud@savoirfairelinux.com>
- *
- *  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 cx.ring.settings.pluginssettings;
-
-import android.graphics.drawable.Drawable;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.ImageView;
-import android.widget.TextView;
-
-import androidx.annotation.NonNull;
-import androidx.recyclerview.widget.RecyclerView;
-
-import java.io.File;
-import java.util.List;
-
-import cx.ring.R;
-import cx.ring.utils.AndroidFileUtils;
-
-public class PathListAdapter extends RecyclerView.Adapter<PathListAdapter.PathViewHolder> {
-    private List<String> mList;
-    private PathListItemListener mListener;
-    public static final String TAG = PathListAdapter.class.getSimpleName();
-
-    PathListAdapter(List<String> pathList, PathListItemListener listener) {
-        mList = pathList;
-        mListener = listener;
-    }
-
-    @NonNull
-    @Override
-    public PathViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
-        View view = LayoutInflater.from(parent.getContext())
-                .inflate(R.layout.frag_path_list_item, parent, false);
-        return new PathViewHolder(view, mListener);
-    }
-
-    @Override
-    public void onBindViewHolder(@NonNull PathViewHolder holder, int position) {
-        holder.setDetails(mList.get(position));
-    }
-
-    @Override
-    public int getItemCount() {
-        return mList.size();
-    }
-
-    public void updatePluginsList(List<String> listPaths) {
-        mList = listPaths;
-        notifyDataSetChanged();
-    }
-
-    class PathViewHolder extends RecyclerView.ViewHolder{
-        private final ImageView pathIcon;
-        private final TextView pathTextView;
-        private String path;
-
-        PathViewHolder(@NonNull View itemView, PathListItemListener listener) {
-            super(itemView);
-            // Views that should be updated by the update method
-            pathIcon = itemView.findViewById(R.id.path_item_icon);
-            pathTextView = itemView.findViewById(R.id.path_item_name);
-
-            // Set listeners, we set the listeners on creation so details can be null
-            itemView.setOnClickListener(v -> listener.onPathItemClicked(path));
-        }
-
-        // update the viewHolder view
-        public void update(String s) {
-            // Set the plugin icon
-            File file = new File(s);
-            if (file.exists()) {
-                if (AndroidFileUtils.isImage(s)) {
-                    pathTextView.setVisibility(View.GONE);
-                    Drawable icon = Drawable.createFromPath(s);
-                    if (icon != null) {
-                        pathIcon.setImageDrawable(icon);
-                    }
-                } else {
-                    pathTextView.setVisibility(View.VISIBLE);
-                    pathTextView.setText(AndroidFileUtils.getFileName(s));
-                }
-            }
-        }
-
-        public void setDetails(String path) {
-            this.path = path;
-            update(this.path);
-        }
-    }
-
-    public interface PathListItemListener {
-        void onPathItemClicked(String path);
-    }
-}
diff --git a/ring-android/app/src/main/java/cx/ring/settings/pluginssettings/PathListAdapter.kt b/ring-android/app/src/main/java/cx/ring/settings/pluginssettings/PathListAdapter.kt
new file mode 100644
index 000000000..e76e54e3e
--- /dev/null
+++ b/ring-android/app/src/main/java/cx/ring/settings/pluginssettings/PathListAdapter.kt
@@ -0,0 +1,93 @@
+/*
+ *  Copyright (C) 2004-2021 Savoir-faire Linux Inc.
+ *
+ *  Author: Aline Gondim Santos <aline.gondimsantos@savoirfairelinux.com>
+ *  Author: Adrien Béraud <adrien.beraud@savoirfairelinux.com>
+ *
+ *  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 cx.ring.settings.pluginssettings
+
+import android.graphics.drawable.Drawable
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import android.widget.ImageView
+import android.widget.TextView
+import androidx.recyclerview.widget.RecyclerView
+import cx.ring.R
+import cx.ring.utils.AndroidFileUtils
+import java.io.File
+
+class PathListAdapter internal constructor(
+    private var mList: List<String>,
+    private val mListener: PathListItemListener
+) : RecyclerView.Adapter<PathListAdapter.PathViewHolder>() {
+    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): PathViewHolder {
+        val view = LayoutInflater.from(parent.context)
+            .inflate(R.layout.frag_path_list_item, parent, false)
+        return PathViewHolder(view, mListener)
+    }
+
+    override fun onBindViewHolder(holder: PathViewHolder, position: Int) {
+        holder.setDetails(mList[position])
+    }
+
+    override fun getItemCount(): Int {
+        return mList.size
+    }
+
+    fun updatePluginsList(listPaths: List<String>) {
+        mList = listPaths
+        notifyDataSetChanged()
+    }
+
+    inner class PathViewHolder(itemView: View, listener: PathListItemListener) : RecyclerView.ViewHolder(itemView) {
+        private val pathIcon: ImageView = itemView.findViewById(R.id.path_item_icon)
+        private val pathTextView: TextView = itemView.findViewById(R.id.path_item_name)
+        private var path: String? = null
+
+        // update the viewHolder view
+        fun update(s: String) {
+            // Set the plugin icon
+            val file = File(s)
+            if (file.exists()) {
+                if (AndroidFileUtils.isImage(s)) {
+                    pathTextView.visibility = View.GONE
+                    Drawable.createFromPath(s)?.let { icon -> pathIcon.setImageDrawable(icon) }
+                } else {
+                    pathTextView.visibility = View.VISIBLE
+                    pathTextView.text = file.name
+                }
+            }
+        }
+
+        fun setDetails(path: String) {
+            this.path = path
+            update(path)
+        }
+
+        init {
+            itemView.setOnClickListener { path?.let { path -> listener.onPathItemClicked(path) }}
+        }
+    }
+
+    interface PathListItemListener {
+        fun onPathItemClicked(path: String)
+    }
+
+    companion object {
+        val TAG = PathListAdapter::class.simpleName!!
+    }
+}
\ No newline at end of file
diff --git a/ring-android/app/src/main/java/cx/ring/settings/pluginssettings/PluginDetails.java b/ring-android/app/src/main/java/cx/ring/settings/pluginssettings/PluginDetails.java
deleted file mode 100644
index 377a70a39..000000000
--- a/ring-android/app/src/main/java/cx/ring/settings/pluginssettings/PluginDetails.java
+++ /dev/null
@@ -1,115 +0,0 @@
-/*
- *  Copyright (C) 2004-2021 Savoir-faire Linux Inc.
- *
- *  Author: Aline Gondim Santos <aline.gondimsantos@savoirfairelinux.com>
- *
- *  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 cx.ring.settings.pluginssettings;
-
-import android.graphics.drawable.Drawable;
-
-import net.jami.daemon.JamiService;
-
-import java.io.File;
-import java.util.List;
-import java.util.Map;
-
-/**
- * Class that contains PluginDetails like name, rootPath
- */
-public class PluginDetails {
-    public static final String TAG = PluginDetails.class.getSimpleName();
-    private String name;
-    private String rootPath;
-    private Map<String, String>  details;
-    private Drawable icon;
-    private boolean enabled;
-    private String mHandlerId;
-
-    public PluginDetails(String name, String rootPath, boolean enabled) {
-        this.name = name;
-        this.rootPath = rootPath;
-        this.enabled = enabled;
-        details = getPluginDetails();
-        setIcon();
-    }
-
-    public PluginDetails(String name, String rootPath, boolean enabled, String handlerId) {
-        this.name = name;
-        this.mHandlerId = handlerId;
-        this.rootPath = rootPath;
-        this.enabled = enabled;
-        details = getPluginDetails();
-        setIcon();
-    }
-
-    public String getName() {
-        return name;
-    }
-
-    public void setName(String name) {
-        this.name = name;
-    }
-
-    public String getRootPath() { return rootPath; }
-
-    public String getmHandlerId() { return mHandlerId; }
-
-    /**
-     * Returns the plugin activation status by the user
-     * @return boolean
-     */
-    public boolean isEnabled() {
-        return enabled;
-    }
-
-    public void setEnabled(boolean enabled) {
-        this.enabled = enabled;
-    }
-
-    public void setIcon() {
-        String  iconPath = details.get("iconPath");
-
-        if (iconPath.endsWith("svg"))
-            iconPath = iconPath.replace(".svg", ".png");
-        if (iconPath != null) {
-            File file = new File(iconPath);
-            if(file.exists()) {
-                icon = Drawable.createFromPath(iconPath);
-            }
-        }
-    }
-
-    public Drawable getIcon() {
-        return icon;
-    }
-
-    public Map<String, String> getPluginDetails() {
-        return JamiService.getPluginDetails(getRootPath()).toNative();
-    }
-
-    public List<Map<String,String>> getPluginPreferences() {
-        return JamiService.getPluginPreferences(getRootPath()).toNative();
-    }
-
-    public Map<String, String> getPluginPreferencesValues() {
-        return JamiService.getPluginPreferencesValues(getRootPath());
-    }
-
-    public boolean setPluginPreference(String key, String value) {
-        return JamiService.setPluginPreference(getRootPath(), key, value);
-    }
-
-}
diff --git a/ring-android/app/src/main/java/cx/ring/settings/pluginssettings/PluginDetails.kt b/ring-android/app/src/main/java/cx/ring/settings/pluginssettings/PluginDetails.kt
new file mode 100644
index 000000000..a5a488ab5
--- /dev/null
+++ b/ring-android/app/src/main/java/cx/ring/settings/pluginssettings/PluginDetails.kt
@@ -0,0 +1,71 @@
+/*
+ *  Copyright (C) 2004-2021 Savoir-faire Linux Inc.
+ *
+ *  Author: Aline Gondim Santos <aline.gondimsantos@savoirfairelinux.com>
+ *
+ *  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 cx.ring.settings.pluginssettings
+
+import android.graphics.drawable.Drawable
+import net.jami.daemon.JamiService
+import java.io.File
+
+/**
+ * Class that contains PluginDetails like name, rootPath
+ */
+class PluginDetails(var name: String, rootPath: String, enabled: Boolean, val handlerId: String? = null) {
+    var rootPath: String = rootPath
+        private set
+    private val details: Map<String, String> = pluginDetails
+    var icon: Drawable? = null
+        private set
+
+    /**
+     * Returns the plugin activation status by the user
+     * @return boolean
+     */
+    var isEnabled: Boolean = enabled
+
+    fun setIcon() {
+        var iconPath = details["iconPath"]
+        if (iconPath != null) {
+            if (iconPath.endsWith("svg"))
+                iconPath = iconPath.replace(".svg", ".png")
+            val file = File(iconPath)
+            if (file.exists()) {
+                icon = Drawable.createFromPath(iconPath)
+            }
+        }
+    }
+
+    private val pluginDetails: Map<String, String>
+        get() = JamiService.getPluginDetails(rootPath).toNative()
+    val pluginPreferences: List<Map<String, String>>
+        get() = JamiService.getPluginPreferences(rootPath).toNative()
+    val pluginPreferencesValues: Map<String, String>
+        get() = JamiService.getPluginPreferencesValues(rootPath)
+
+    fun setPluginPreference(key: String, value: String): Boolean {
+        return JamiService.setPluginPreference(rootPath, key, value)
+    }
+
+    companion object {
+        val TAG = PluginDetails::class.simpleName!!
+    }
+
+    init {
+        setIcon()
+    }
+}
\ No newline at end of file
diff --git a/ring-android/app/src/main/java/cx/ring/settings/pluginssettings/PluginPathPreferenceFragment.java b/ring-android/app/src/main/java/cx/ring/settings/pluginssettings/PluginPathPreferenceFragment.java
deleted file mode 100644
index 56c1d2574..000000000
--- a/ring-android/app/src/main/java/cx/ring/settings/pluginssettings/PluginPathPreferenceFragment.java
+++ /dev/null
@@ -1,210 +0,0 @@
-/*
- *  Copyright (C) 2004-2021 Savoir-faire Linux Inc.
- *
- *  Author: Aline Gondim Santos <aline.gondimsantos@savoirfairelinux.com>
- *
- *  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 cx.ring.settings.pluginssettings;
-
-import android.app.Activity;
-import android.content.Context;
-import android.content.Intent;
-import android.graphics.drawable.Drawable;
-import android.net.Uri;
-import android.os.Bundle;
-import android.text.TextUtils;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.Toast;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.fragment.app.Fragment;
-
-import java.io.File;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Map;
-
-import cx.ring.R;
-import cx.ring.client.HomeActivity;
-import cx.ring.databinding.FragPluginsPathPreferenceBinding;
-import cx.ring.utils.AndroidFileUtils;
-import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
-
-public class PluginPathPreferenceFragment extends Fragment implements PathListAdapter.PathListItemListener {
-
-    public static final String TAG = PluginPathPreferenceFragment.class.getSimpleName();
-    private static final int PATH_REQUEST_CODE = 1;
-    private final List<String> pathList = new ArrayList<>();
-    private PluginDetails mPluginDetails;
-    private String mCurrentKey;
-    private String mCurrentValue;
-    private Context mContext;
-    private String subtitle;
-    private String[] supportedMimeTypes = {"*/*"};
-
-    private FragPluginsPathPreferenceBinding binding;
-
-    public static PluginPathPreferenceFragment newInstance(PluginDetails pluginDetails, String preferenceKey) {
-        Bundle args = new Bundle();
-        args.putString("name", pluginDetails.getName());
-        args.putString("rootPath", pluginDetails.getRootPath());
-        args.putBoolean("enabled", pluginDetails.isEnabled());
-        args.putString("preferenceKey", preferenceKey);
-        PluginPathPreferenceFragment ppf = new PluginPathPreferenceFragment();
-        ppf.setArguments(args);
-        return ppf;
-    }
-
-    @Override
-    public void onCreate(@Nullable Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-        mContext = requireActivity();
-
-        Bundle arguments = getArguments();
-
-        if (arguments != null && arguments.getString("name") != null
-                && arguments.getString("rootPath") != null) {
-            mPluginDetails = new PluginDetails(arguments.getString("name"), arguments.getString("rootPath"), arguments.getBoolean("enabled"));
-
-            List<Map<String, String>> mPreferencesAttributes = mPluginDetails.getPluginPreferences();
-            if (mPreferencesAttributes != null && !mPreferencesAttributes.isEmpty()) {
-                mCurrentKey = arguments.getString("preferenceKey");
-                mCurrentValue = mPluginDetails.getPluginPreferencesValues().get(mCurrentKey);
-                setHasOptionsMenu(true);
-                for (Map<String, String> preferenceAttributes : mPreferencesAttributes) {
-                    if (preferenceAttributes.get("key").equals(mCurrentKey)) {
-                        String mimeType = preferenceAttributes.get("mimeType");
-                        if (!TextUtils.isEmpty(mimeType))
-                            supportedMimeTypes = mimeType.split(",");
-                        subtitle = mPluginDetails.getName() + " - " + preferenceAttributes.get("title");
-
-                        String defaultPath = preferenceAttributes.get("defaultValue");
-                        if (!TextUtils.isEmpty(defaultPath)) {
-                            defaultPath = defaultPath.substring(0, defaultPath.lastIndexOf("/"));
-                            for (File file : new File(defaultPath).listFiles()) {
-                                pathList.add(file.toString());
-                            }
-                            break;
-                        }
-                    }
-                }
-            }
-        }
-    }
-
-    @Nullable
-    @Override
-    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
-                             @Nullable Bundle savedInstanceState) {
-        binding = FragPluginsPathPreferenceBinding.inflate(inflater, container, false);
-
-        if (!pathList.isEmpty()) {
-            binding.pathPreferences.setAdapter(new PathListAdapter(pathList, this));
-        }
-
-        ((HomeActivity) requireActivity()).setToolbarTitle(R.string.menu_item_plugin_list);
-
-        return binding.getRoot();
-    }
-
-    @Override
-    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
-
-        binding.pluginSettingSubtitle.setText(subtitle);
-        binding.pluginsPathPreferenceFab.setOnClickListener(v -> {
-            Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
-            intent.addCategory(Intent.CATEGORY_OPENABLE);
-            intent.setType(supportedMimeTypes[0]);
-            intent.putExtra(Intent.EXTRA_MIME_TYPES, supportedMimeTypes);
-            startActivityForResult(intent, PATH_REQUEST_CODE);
-        });
-
-        if (!mCurrentValue.isEmpty()) {
-            binding.currentPathItemIcon.setVisibility(View.VISIBLE);
-            File file = new File(mCurrentValue);
-            if (file.exists()) {
-                if (AndroidFileUtils.isImage(mCurrentValue)) {
-                    binding.currentPathItemName.setVisibility(View.INVISIBLE);
-                    Drawable icon = Drawable.createFromPath(mCurrentValue);
-                    if (icon != null) {
-                        binding.currentPathItemIcon.setImageDrawable(icon);
-                    }
-                } else {
-                    binding.currentPathItemName.setVisibility(View.VISIBLE);
-                    binding.currentPathItemName.setText(AndroidFileUtils.getFileName(mCurrentValue));
-                }
-            }
-        } else {
-            binding.currentPathItemIcon.setVisibility(View.INVISIBLE);
-            binding.currentPathItemName.setVisibility(View.INVISIBLE);
-            binding.pluginsPathPreferenceFab.performClick();
-        }
-    }
-
-    @Override
-    public void onActivityResult(int requestCode, int resultCode, Intent data) {
-        if (requestCode == PATH_REQUEST_CODE && resultCode == Activity.RESULT_OK) {
-            if (data != null) {
-                Uri uri = data.getData();
-                if (uri != null) {
-                    AndroidFileUtils.getCacheFile(requireContext(), uri)
-                            .observeOn(AndroidSchedulers.mainThread())
-                            .subscribe(file -> setPreferencePath(file.getAbsolutePath()),
-                                    e -> Toast.makeText(mContext, e.getMessage(), Toast.LENGTH_LONG).show());
-                }
-            }
-        }
-    }
-
-    @Override
-    public void onResume() {
-        ((HomeActivity) requireActivity()).setToolbarTitle(R.string.menu_item_plugin_list);
-        super.onResume();
-    }
-
-    @Override
-    public void onDestroyView() {
-        binding = null;
-        super.onDestroyView();
-    }
-
-    private void setPreferencePath(String path) {
-        if (mPluginDetails.setPluginPreference(mCurrentKey, path)) {
-            mCurrentValue = path;
-            if (!path.isEmpty()) {
-                binding.currentPathItemIcon.setVisibility(View.VISIBLE);
-                if (AndroidFileUtils.isImage(mCurrentValue)) {
-                    Drawable icon = Drawable.createFromPath(mCurrentValue);
-                    binding.currentPathItemIcon.setImageDrawable(icon);
-                    binding.currentPathItemName.setVisibility(View.INVISIBLE);
-                } else {
-                    binding.currentPathItemName.setText(AndroidFileUtils.getFileName(mCurrentValue));
-                    binding.currentPathItemName.setVisibility(View.VISIBLE);
-                }
-            } else {
-                binding.currentPathItemIcon.setVisibility(View.INVISIBLE);
-                binding.currentPathItemName.setVisibility(View.INVISIBLE);
-            }
-        }
-    }
-
-    @Override
-    public void onPathItemClicked(String path) {
-        setPreferencePath(path);
-    }
-}
diff --git a/ring-android/app/src/main/java/cx/ring/settings/pluginssettings/PluginPathPreferenceFragment.kt b/ring-android/app/src/main/java/cx/ring/settings/pluginssettings/PluginPathPreferenceFragment.kt
new file mode 100644
index 000000000..4c3649ccc
--- /dev/null
+++ b/ring-android/app/src/main/java/cx/ring/settings/pluginssettings/PluginPathPreferenceFragment.kt
@@ -0,0 +1,179 @@
+/*
+ *  Copyright (C) 2004-2021 Savoir-faire Linux Inc.
+ *
+ *  Author: Aline Gondim Santos <aline.gondimsantos@savoirfairelinux.com>
+ *
+ *  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 cx.ring.settings.pluginssettings
+
+import android.app.Activity
+import android.content.Intent
+import android.graphics.drawable.Drawable
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import android.widget.Toast
+import androidx.fragment.app.Fragment
+import cx.ring.R
+import cx.ring.client.HomeActivity
+import cx.ring.databinding.FragPluginsPathPreferenceBinding
+import cx.ring.settings.pluginssettings.PathListAdapter.PathListItemListener
+import cx.ring.utils.AndroidFileUtils
+import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
+import java.io.File
+
+class PluginPathPreferenceFragment : Fragment(), PathListItemListener {
+    private val pathList: MutableList<String> = ArrayList()
+    private lateinit var mPluginDetails: PluginDetails
+    private lateinit var mCurrentKey: String
+    private var mCurrentValue: String? = null
+    private var subtitle: String = ""
+    private var supportedMimeTypes = arrayOf("*/*")
+    private var binding: FragPluginsPathPreferenceBinding? = null
+
+    override fun onCreate(savedInstanceState: Bundle?) {
+        super.onCreate(savedInstanceState)
+        val arguments = requireArguments()
+        val details = PluginDetails(arguments.getString("name")!!, arguments.getString("rootPath")!!, arguments.getBoolean("enabled"))
+        mPluginDetails = details
+        val key = arguments.getString("preferenceKey")!!
+        mCurrentKey = key
+        val mPreferencesAttributes = details.pluginPreferences
+        if (mPreferencesAttributes.isNotEmpty()) {
+            mCurrentValue = details.pluginPreferencesValues[key]
+            setHasOptionsMenu(true)
+            for (preferenceAttributes in mPreferencesAttributes) {
+                if (preferenceAttributes["key"] == key) {
+                    val mimeType = preferenceAttributes["mimeType"]
+                    if (mimeType != null && mimeType.isNotEmpty())
+                        supportedMimeTypes = mimeType.split(',').toTypedArray()
+                    subtitle = details.name + " - " + preferenceAttributes["title"]
+                    var defaultPath = preferenceAttributes["defaultValue"]
+                    if (defaultPath != null && defaultPath.isNotEmpty()) {
+                        defaultPath = defaultPath.substring(0, defaultPath.lastIndexOf("/"))
+                        for (file in File(defaultPath).listFiles()!!)
+                            pathList.add(file.toString())
+                        break
+                    }
+                }
+            }
+        }
+    }
+
+    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
+        (requireActivity() as HomeActivity).setToolbarTitle(R.string.menu_item_plugin_list)
+        return FragPluginsPathPreferenceBinding.inflate(inflater, container, false).apply {
+            if (pathList.isNotEmpty())
+                pathPreferences.adapter = PathListAdapter(pathList, this@PluginPathPreferenceFragment)
+            binding = this
+            pluginSettingSubtitle.text = subtitle
+            pluginsPathPreferenceFab.setOnClickListener {
+                val intent = Intent(Intent.ACTION_OPEN_DOCUMENT).apply {
+                    addCategory(Intent.CATEGORY_OPENABLE)
+                    type = supportedMimeTypes[0]
+                    putExtra(Intent.EXTRA_MIME_TYPES, supportedMimeTypes)
+                }
+                startActivityForResult(intent, PATH_REQUEST_CODE)
+            }
+        }.root
+    }
+
+    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+        val currentValue = mCurrentValue
+        val binding = binding ?: return
+        if (currentValue != null && currentValue.isNotEmpty()) {
+            binding.currentPathItemIcon.visibility = View.VISIBLE
+            val file = File(currentValue)
+            if (file.exists()) {
+                if (AndroidFileUtils.isImage(currentValue)) {
+                    binding.currentPathItemName.visibility = View.INVISIBLE
+                    val icon = Drawable.createFromPath(currentValue)
+                    if (icon != null) {
+                        binding.currentPathItemIcon.setImageDrawable(icon)
+                    }
+                } else {
+                    binding.currentPathItemName.visibility = View.VISIBLE
+                    binding.currentPathItemName.text = file.name
+                }
+            }
+        } else {
+            binding.currentPathItemIcon.visibility = View.INVISIBLE
+            binding.currentPathItemName.visibility = View.INVISIBLE
+            binding.pluginsPathPreferenceFab.performClick()
+        }
+    }
+
+    override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
+        if (requestCode == PATH_REQUEST_CODE && resultCode == Activity.RESULT_OK) {
+            data?.data?.let { uri ->
+                AndroidFileUtils.getCacheFile(requireContext(), uri)
+                    .observeOn(AndroidSchedulers.mainThread())
+                    .subscribe({ file: File -> setPreferencePath(file.absolutePath) })
+                    { e: Throwable -> context?.let { c -> Toast.makeText(c, e.message, Toast.LENGTH_LONG).show() }}
+            }
+        }
+    }
+
+    override fun onResume() {
+        (requireActivity() as HomeActivity).setToolbarTitle(R.string.menu_item_plugin_list)
+        super.onResume()
+    }
+
+    override fun onDestroyView() {
+        binding = null
+        super.onDestroyView()
+    }
+
+    private fun setPreferencePath(path: String) {
+        if (mPluginDetails.setPluginPreference(mCurrentKey, path)) {
+            mCurrentValue = path
+            val binding = binding ?: return
+            if (path.isNotEmpty()) {
+                binding.currentPathItemIcon.visibility = View.VISIBLE
+                if (AndroidFileUtils.isImage(path)) {
+                    val icon = Drawable.createFromPath(path)
+                    binding.currentPathItemIcon.setImageDrawable(icon)
+                    binding.currentPathItemName.visibility = View.INVISIBLE
+                } else {
+                    binding.currentPathItemName.text = File(path).name
+                    binding.currentPathItemName.visibility = View.VISIBLE
+                }
+            } else {
+                binding.currentPathItemIcon.visibility = View.INVISIBLE
+                binding.currentPathItemName.visibility = View.INVISIBLE
+            }
+        }
+    }
+
+    override fun onPathItemClicked(path: String) {
+        setPreferencePath(path)
+    }
+
+    companion object {
+        val TAG = PluginPathPreferenceFragment::class.simpleName!!
+        private const val PATH_REQUEST_CODE = 1
+        fun newInstance(pluginDetails: PluginDetails, preferenceKey: String?): PluginPathPreferenceFragment {
+            val ppf = PluginPathPreferenceFragment()
+            ppf.arguments = Bundle().apply {
+                putString("name", pluginDetails.name)
+                putString("rootPath", pluginDetails.rootPath)
+                putBoolean("enabled", pluginDetails.isEnabled)
+                putString("preferenceKey", preferenceKey)
+            }
+            return ppf
+        }
+    }
+}
\ No newline at end of file
diff --git a/ring-android/app/src/main/java/cx/ring/settings/pluginssettings/PluginPreferencesDataStore.java b/ring-android/app/src/main/java/cx/ring/settings/pluginssettings/PluginPreferencesDataStore.java
deleted file mode 100644
index 67020ff32..000000000
--- a/ring-android/app/src/main/java/cx/ring/settings/pluginssettings/PluginPreferencesDataStore.java
+++ /dev/null
@@ -1,186 +0,0 @@
-package cx.ring.settings.pluginssettings;
-
-import androidx.annotation.Nullable;
-import androidx.preference.PreferenceDataStore;
-
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Map;
-import java.util.Set;
-import java.util.concurrent.locks.Lock;
-import java.util.concurrent.locks.ReadWriteLock;
-import java.util.concurrent.locks.ReentrantReadWriteLock;
-
-import cx.ring.plugins.PluginUtils;
-
-public class PluginPreferencesDataStore extends PreferenceDataStore {
-
-    private PluginDetails mPluginDetails;
-    private Map<String, String> mPreferenceTypes = new HashMap<>();
-    private Map<String, String> preferencesValues;
-    private ReadWriteLock lock = new ReentrantReadWriteLock();
-
-    public PluginPreferencesDataStore(PluginDetails pluginDetails) {
-        mPluginDetails = pluginDetails;
-        preferencesValues = mPluginDetails.getPluginPreferencesValues();
-    }
-
-    public void addTomPreferenceTypes(Map<String, String> preferenceModel) {
-        Lock writeLock = lock.writeLock();
-        try {
-            writeLock.lock();
-            mPreferenceTypes.put(preferenceModel.get("key"), preferenceModel.get("type"));
-        } finally {
-            writeLock.unlock();
-        }
-    }
-
-    @Override
-    public void putBoolean(String key, boolean value) {
-        boolean success = mPluginDetails.setPluginPreference(key, value ? "1" : "0");
-        if(success) {
-            notifyPreferencesValuesChange();
-        }
-    }
-
-    @Override
-    public void putString(String key, @Nullable String value) {
-        boolean success = mPluginDetails.setPluginPreference(key, value);
-        if(success) {
-            notifyPreferencesValuesChange();
-        }
-    }
-
-    @Override
-    public void putStringSet(String key, @Nullable Set<String> values) {
-        if(values != null) {
-            boolean success = mPluginDetails.setPluginPreference(key,
-                    PluginUtils.listStringToStringList(new ArrayList<>(values)));
-            if(success) {
-                notifyPreferencesValuesChange();
-            }
-        }
-    }
-
-    @Override
-    public void putInt(String key, int value) {
-        Integer boxedValue = value;
-        boolean success = mPluginDetails.setPluginPreference(key, boxedValue.toString());
-        if(success) {
-            notifyPreferencesValuesChange();
-        }
-    }
-
-    @Override
-    public void putLong(String key, long value) {
-        Long boxedValue = value;
-        boolean success = mPluginDetails.setPluginPreference(key, boxedValue.toString());
-        if(success) {
-            notifyPreferencesValuesChange();
-        }
-    }
-
-    @Override
-    public void putFloat(String key, float value) {
-        Float boxedValue = value;
-        boolean success = mPluginDetails.setPluginPreference(key, boxedValue.toString());
-        if(success) {
-            notifyPreferencesValuesChange();
-        }
-    }
-
-    @Nullable
-    @Override
-    public String getString(String key, @Nullable String defValue) {
-        String returnValue = defValue;
-        String value = getPreferencesValues().get(key);
-        if (value != null) {
-            returnValue = value;
-        }
-        return returnValue;
-    }
-
-    @Nullable
-    @Override
-    public Set<String> getStringSet(String key, @Nullable Set<String> defValues) {
-        Set<String> returnValue =  defValues;
-
-        String value = getPreferencesValues().get(key);
-
-        if(value != null) {
-            returnValue = new HashSet<>(PluginUtils.stringListToListString(value));
-        }
-
-        return returnValue;
-    }
-
-    @Override
-    public int getInt(String key, int defValue) {
-        int returnValue = defValue;
-        String value = getPreferencesValues().get(key);
-        if (value != null) {
-            returnValue = Integer.parseInt(value);
-        }
-        return returnValue;
-    }
-
-    @Override
-    public long getLong(String key, long defValue) {
-        long returnValue = defValue;
-        String value = getPreferencesValues().get(key);
-        if (value != null) {
-            returnValue = Long.parseLong(value);
-        }
-        return returnValue;
-    }
-
-    @Override
-    public float getFloat(String key, float defValue) {
-        float returnValue = defValue;
-        String value = getPreferencesValues().get(key);
-        if (value != null) {
-            returnValue = Float.parseFloat(value);
-        }
-        return returnValue;
-    }
-
-    @Override
-    public boolean getBoolean(String key, boolean defValue) {
-        boolean returnValue = defValue;
-        String value = getPreferencesValues().get(key);
-        if (value != null) {
-            returnValue = value.equals("1");
-        }
-        return returnValue;
-    }
-
-    /**
-     *  Updates the preferencesValues map
-     *  Use locks since the PreferenceInteraction is asynchronous
-     */
-    public void notifyPreferencesValuesChange() {
-        Lock writeLock = lock.writeLock();
-        try {
-            writeLock.lock();
-            preferencesValues = mPluginDetails.getPluginPreferencesValues();
-        } finally {
-            writeLock.unlock();
-        }
-    }
-
-    /**
-     * Returns the preferencesValues
-     * Use locks since the PreferenceInteraction is asynchronous
-     * @return preferencesValues
-     */
-    private Map<String, String>  getPreferencesValues() {
-        Lock readLock = lock.readLock();
-        try {
-            readLock.lock();
-            return preferencesValues;
-        } finally {
-            readLock.unlock();
-        }
-    }
-}
diff --git a/ring-android/app/src/main/java/cx/ring/settings/pluginssettings/PluginPreferencesDataStore.kt b/ring-android/app/src/main/java/cx/ring/settings/pluginssettings/PluginPreferencesDataStore.kt
new file mode 100644
index 000000000..77502d4a9
--- /dev/null
+++ b/ring-android/app/src/main/java/cx/ring/settings/pluginssettings/PluginPreferencesDataStore.kt
@@ -0,0 +1,118 @@
+package cx.ring.settings.pluginssettings
+
+import androidx.preference.PreferenceDataStore
+import java.util.concurrent.locks.ReadWriteLock
+import java.util.concurrent.locks.ReentrantReadWriteLock
+
+class PluginPreferencesDataStore(private val mPluginDetails: PluginDetails) : PreferenceDataStore() {
+    private val mPreferenceTypes: MutableMap<String, String> = HashMap()
+
+    private var preferencesValues: Map<String, String> = mPluginDetails.pluginPreferencesValues
+    private val lock: ReadWriteLock = ReentrantReadWriteLock()
+
+    fun addToPreferenceTypes(preferenceModel: Map<String, String>) {
+        val writeLock = lock.writeLock()
+        try {
+            writeLock.lock()
+            mPreferenceTypes[preferenceModel["key"]!!] = preferenceModel["type"]!!
+        } finally {
+            writeLock.unlock()
+        }
+    }
+
+    override fun putBoolean(key: String, value: Boolean) {
+        if (mPluginDetails.setPluginPreference(key, if (value) "1" else "0")) {
+            notifyPreferencesValuesChange()
+        }
+    }
+
+    override fun putString(key: String, value: String?) {
+        if (mPluginDetails.setPluginPreference(key, value ?: "")) {
+            notifyPreferencesValuesChange()
+        }
+    }
+
+    override fun putStringSet(key: String, values: Set<String>?) {
+        if (values != null) {
+            if (mPluginDetails.setPluginPreference(key, values.joinToString(","))) {
+                notifyPreferencesValuesChange()
+            }
+        }
+    }
+
+    override fun putInt(key: String, value: Int) {
+        if (mPluginDetails.setPluginPreference(key, value.toString())) {
+            notifyPreferencesValuesChange()
+        }
+    }
+
+    override fun putLong(key: String, value: Long) {
+        if (mPluginDetails.setPluginPreference(key, value.toString())) {
+            notifyPreferencesValuesChange()
+        }
+    }
+
+    override fun putFloat(key: String, value: Float) {
+        if (mPluginDetails.setPluginPreference(key, value.toString())) {
+            notifyPreferencesValuesChange()
+        }
+    }
+
+    override fun getString(key: String, defValue: String?): String? {
+        return getPreferencesValues()[key] ?: defValue
+    }
+
+    override fun getStringSet(key: String, defValues: Set<String>?): Set<String>? {
+        return getPreferencesValues()[key]?.split(',')?.toSet() ?: defValues
+    }
+
+    override fun getInt(key: String, defValue: Int): Int {
+        return getPreferencesValues()[key]?.toInt() ?: defValue
+    }
+
+    override fun getLong(key: String, defValue: Long): Long {
+        return getPreferencesValues()[key]?.toLong() ?: defValue
+    }
+
+    override fun getFloat(key: String, defValue: Float): Float {
+        return getPreferencesValues()[key]?.toFloat() ?: defValue
+    }
+
+    override fun getBoolean(key: String, defValue: Boolean): Boolean {
+        var returnValue = defValue
+        val value = getPreferencesValues()[key]
+        if (value != null) {
+            returnValue = value == "1"
+        }
+        return returnValue
+    }
+
+    /**
+     * Updates the preferencesValues map
+     * Use locks since the PreferenceInteraction is asynchronous
+     */
+    private fun notifyPreferencesValuesChange() {
+        val writeLock = lock.writeLock()
+        preferencesValues = try {
+            writeLock.lock()
+            mPluginDetails.pluginPreferencesValues
+        } finally {
+            writeLock.unlock()
+        }
+    }
+
+    /**
+     * Returns the preferencesValues
+     * Use locks since the PreferenceInteraction is asynchronous
+     * @return preferencesValues
+     */
+    private fun getPreferencesValues(): Map<String, String> {
+        val readLock = lock.readLock()
+        return try {
+            readLock.lock()
+            preferencesValues
+        } finally {
+            readLock.unlock()
+        }
+    }
+}
\ No newline at end of file
diff --git a/ring-android/app/src/main/java/cx/ring/settings/pluginssettings/PluginSettingsFragment.java b/ring-android/app/src/main/java/cx/ring/settings/pluginssettings/PluginSettingsFragment.java
deleted file mode 100644
index daed74a8e..000000000
--- a/ring-android/app/src/main/java/cx/ring/settings/pluginssettings/PluginSettingsFragment.java
+++ /dev/null
@@ -1,382 +0,0 @@
-package cx.ring.settings.pluginssettings;
-
-import android.content.Context;
-import android.os.Bundle;
-import android.util.Log;
-import android.view.LayoutInflater;
-import android.view.Menu;
-import android.view.MenuInflater;
-import android.view.MenuItem;
-import android.view.View;
-import android.view.ViewGroup;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.preference.CheckBoxPreference;
-import androidx.preference.DialogPreference;
-import androidx.preference.DropDownPreference;
-import androidx.preference.EditTextPreference;
-import androidx.preference.ListPreference;
-import androidx.preference.MultiSelectListPreference;
-import androidx.preference.Preference;
-import androidx.preference.PreferenceFragmentCompat;
-import androidx.preference.PreferenceManager;
-import androidx.preference.PreferenceScreen;
-import androidx.preference.SeekBarPreference;
-import androidx.preference.SwitchPreference;
-import androidx.preference.TwoStatePreference;
-
-import com.google.android.material.dialog.MaterialAlertDialogBuilder;
-
-import net.jami.daemon.JamiService;
-
-import java.util.ArrayList;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
-import cx.ring.R;
-import cx.ring.client.HomeActivity;
-import cx.ring.plugins.PluginPreferences;
-import cx.ring.plugins.PluginUtils;
-
-public class PluginSettingsFragment extends PreferenceFragmentCompat {
-    public static final String TAG = PluginSettingsFragment.class.getSimpleName();
-    private Context mContext;
-    private List<Map<String, String>> mPreferencesAttributes;
-    private PluginDetails pluginDetails;
-    private PluginPreferencesDataStore ppds;
-
-    public static PluginSettingsFragment newInstance(PluginDetails pluginDetails) {
-        Bundle args = new Bundle();
-        args.putString("name", pluginDetails.getName());
-        args.putString("rootPath", pluginDetails.getRootPath());
-        args.putBoolean("enabled", pluginDetails.isEnabled());
-        PluginSettingsFragment psf = new PluginSettingsFragment();
-        psf.setArguments(args);
-        return psf;
-    }
-
-    @Override
-    public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
-
-    }
-
-    @Override
-    public void onCreate(@Nullable Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-        mContext = requireActivity();
-
-        Bundle arguments = getArguments();
-
-        if (arguments != null && arguments.getString("name") != null
-                && arguments.getString("rootPath") != null) {
-            pluginDetails = new PluginDetails(arguments.getString("name"),
-                    arguments.getString("rootPath"), arguments.getBoolean("enabled"));
-
-            mPreferencesAttributes = pluginDetails.getPluginPreferences();
-
-            PreferenceManager preferenceManager = getPreferenceManager();
-            ppds = new PluginPreferencesDataStore(pluginDetails);
-            preferenceManager.setPreferenceDataStore(ppds);
-            setHasOptionsMenu(true);
-        }
-    }
-
-    @Override
-    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
-                             @Nullable Bundle savedInstanceState) {
-        View root = super.onCreateView(inflater, container, savedInstanceState);
-
-        PreferenceScreen screen = getPreferenceManager().createPreferenceScreen(mContext);
-        screen.addPreference(createHeadPreference());
-        for (Preference preference : createPreferences(mPreferencesAttributes)) {
-            screen.addPreference(preference);
-        }
-        setPreferenceScreen(screen);
-        return root;
-    }
-
-    @Override
-    public void onCreateOptionsMenu(@NonNull Menu menu, @NonNull MenuInflater inflater) {
-        inflater.inflate(R.menu.plugin_edition, menu);
-        MenuItem item = menu.findItem(R.id.menuitem_delete);
-        item.setVisible(false);
-        super.onCreateOptionsMenu(menu, inflater);
-    }
-
-    /**
-     * Takes a list of preferences attributes map
-     * Creates a preference View for each map of attributes in the preference
-     *
-     * @param preferencesAttributes A list of preferences attributes
-     * @return list of preferences
-     */
-    private List<Preference> createPreferences(List<Map<String, String>> preferencesAttributes) {
-        List<Preference> preferencesViews = new ArrayList<>();
-        if (preferencesAttributes != null) {
-            for (Map<String, String> preferenceAttributes : preferencesAttributes) {
-                String type = preferenceAttributes.get("type");
-                // Call for each type the appropriate function member
-                if (type != null) {
-                    switch (type) {
-                        case "CheckBox":
-                            preferencesViews.add(createCheckBoxPreference(preferenceAttributes));
-                            break;
-                        case "DropDown":
-                            preferencesViews.add(createDropDownPreference(preferenceAttributes));
-                            break;
-                        case "EditText":
-                            preferencesViews.add(createEditTextPreference(preferenceAttributes));
-                            break;
-                        case "List":
-                            preferencesViews.add(createListPreference(preferenceAttributes));
-                            break;
-                        case "Path":
-                            preferencesViews.add(createPathPreference(preferenceAttributes));
-                            break;
-                        case "MultiSelectList":
-                            preferencesViews.
-                                    add(createMultiSelectListPreference(preferenceAttributes));
-                            break;
-                        case "SeekBar":
-                            preferencesViews.add(createSeekBarPreference(preferenceAttributes));
-                            break;
-                        case "Switch":
-                            preferencesViews.add(createSwitchPreference(preferenceAttributes));
-                            break;
-                        default:
-                            break;
-                    }
-                }
-            }
-        }
-
-        return preferencesViews;
-    }
-
-    private Preference createHeadPreference()
-    {
-        PluginPreferences preference = new PluginPreferences(mContext, pluginDetails);
-        preference.setResetClickListener(v -> new MaterialAlertDialogBuilder(mContext)
-                .setTitle(preference.getTitle())
-                .setMessage(R.string.plugin_reset_preferences_ask)
-                .setPositiveButton(android.R.string.ok, (dialog, id) -> {
-                    JamiService.resetPluginPreferencesValues(pluginDetails.getRootPath());
-                    ((HomeActivity) requireActivity()).popFragmentImmediate();
-                })
-                .setNegativeButton(android.R.string.cancel, (dialog, whichButton) -> {
-                    /* Terminate with no action */
-                })
-                .show());
-        preference.setInstallClickListener(v -> new MaterialAlertDialogBuilder(mContext)
-                .setMessage(R.string.account_delete_dialog_message)
-                .setTitle(R.string.plugin_uninstall_title)
-                .setPositiveButton(android.R.string.ok, (dialog, whichButton) -> {
-                    pluginDetails.setEnabled(false);
-                    JamiService.uninstallPlugin(pluginDetails.getRootPath());
-                    ((HomeActivity) requireActivity()).popFragmentImmediate();
-                })
-                .setNegativeButton(android.R.string.cancel, null)
-                .show());
-        return preference;
-    }
-
-    private CheckBoxPreference createCheckBoxPreference(Map<String, String> preferenceModel) {
-        CheckBoxPreference preference = new CheckBoxPreference(mContext);
-        setPreferenceAttributes(preference, preferenceModel);
-        setTwoStatePreferenceAttributes(preference, preferenceModel);
-        ppds.addTomPreferenceTypes(preferenceModel);
-        return preference;
-    }
-
-    private DropDownPreference createDropDownPreference(Map<String, String> preferenceModel) {
-        DropDownPreference preference = new DropDownPreference(mContext);
-        setPreferenceAttributes(preference, preferenceModel);
-        setListPreferenceAttributes(preference, preferenceModel);
-        ppds.addTomPreferenceTypes(preferenceModel);
-        return preference;
-    }
-
-    private EditTextPreference createEditTextPreference(Map<String, String> preferenceModel) {
-        EditTextPreference preference = new EditTextPreference(mContext);
-        setPreferenceAttributes(preference, preferenceModel);
-        setDialogPreferenceAttributes(preference, preferenceModel);
-        setEditTextAttributes(preference, preferenceModel);
-        ppds.addTomPreferenceTypes(preferenceModel);
-        return preference;
-    }
-
-    private ListPreference createListPreference(Map<String, String> preferenceModel) {
-        ListPreference preference = new ListPreference(mContext);
-        setPreferenceAttributes(preference, preferenceModel);
-        setListPreferenceAttributes(preference, preferenceModel);
-        ppds.addTomPreferenceTypes(preferenceModel);
-        return preference;
-    }
-
-    private Preference createPathPreference(Map<String, String> preferenceModel){
-        Preference preference = new Preference(mContext);
-        preference.setOnPreferenceClickListener(p -> {
-            ((HomeActivity) mContext).gotToPluginPathPreference(pluginDetails, preferenceModel.get("key"));
-            return false;
-        });
-        setPreferenceAttributes(preference, preferenceModel);
-        ppds.addTomPreferenceTypes(preferenceModel);
-        return preference;
-    }
-
-    private MultiSelectListPreference createMultiSelectListPreference(
-            Map<String, String> preferenceModel) {
-        MultiSelectListPreference preference = new MultiSelectListPreference(mContext);
-        setPreferenceAttributes(preference, preferenceModel);
-        setMultiSelectListPreferenceAttributes(preference, preferenceModel);
-        return preference;
-    }
-
-    private SwitchPreference createSwitchPreference(Map<String, String> preferenceModel) {
-        SwitchPreference preference = new SwitchPreference(mContext);
-        setPreferenceAttributes(preference, preferenceModel);
-        setTwoStatePreferenceAttributes(preference, preferenceModel);
-        return preference;
-    }
-
-    private SeekBarPreference createSeekBarPreference(Map<String, String> preferenceModel) {
-        SeekBarPreference preference = new SeekBarPreference(mContext);
-        setPreferenceAttributes(preference, preferenceModel);
-        setSeekBarPreferenceAttributes(preference, preferenceModel);
-        return preference;
-    }
-
-    /**
-     * Sets attributes that are common in the Preference base class
-     *
-     * @param preference      the preference
-     * @param preferenceModel the map of attributes
-     */
-    private void setPreferenceAttributes(Preference preference,
-                                         Map<String, String> preferenceModel) {
-        String key = preferenceModel.get("key");
-        preference.setKey(key);
-        preference.setTitle(PluginUtils.getOrElse(preferenceModel.get("title"), ""));
-        preference.setSummary(PluginUtils.getOrElse(preferenceModel.get("summary"), ""));
-    }
-
-    //TODO : add drawable icon
-    private void setDialogPreferenceAttributes(DialogPreference preference,
-                                               Map<String, String> preferenceModel) {
-        String dialogTitle = PluginUtils.getOrElse(preferenceModel.get("dialogTitle"), "");
-        String dialogMessage = PluginUtils.getOrElse(preferenceModel.get("dialogMessage"), "");
-        String positiveButtonText = PluginUtils.getOrElse(preferenceModel.get("positiveButtonText"), "");
-        String negativeButtonText = PluginUtils.getOrElse(preferenceModel.get("negativeButtonText"), "");
-
-        if (!dialogTitle.isEmpty()) {
-            preference.setDialogTitle(dialogTitle);
-        }
-
-        if (!dialogMessage.isEmpty()) {
-            preference.setDialogTitle(dialogMessage);
-        }
-
-        if (!positiveButtonText.isEmpty()) {
-            preference.setPositiveButtonText(positiveButtonText);
-        }
-
-        if (!negativeButtonText.isEmpty()) {
-            preference.setNegativeButtonText(negativeButtonText);
-        }
-    }
-
-    /**
-     * Sets attributes specific to EditTextPreference
-     * Here we set the default value
-     *
-     * @param preference      EditTextPreference
-     * @param preferenceModel the map of attributes
-     */
-    private void setEditTextAttributes(EditTextPreference preference,
-                                       Map<String, String> preferenceModel) {
-        preference.setDefaultValue(preferenceModel.get("defaultValue"));
-    }
-
-    /**
-     * Sets specific attributes for Preference that have for a base class ListPreference
-     * Sets the entries, entryValues and defaultValue
-     *
-     * @param preference      the list preference
-     * @param preferenceModel the map of attributes
-     */
-    private void setListPreferenceAttributes(ListPreference preference,
-                                             Map<String, String> preferenceModel) {
-        setDialogPreferenceAttributes(preference, preferenceModel);
-        String entries = PluginUtils.getOrElse(preferenceModel.get("entries"), "");
-        preference.setEntries(PluginUtils.stringListToListString(entries).
-                toArray(new CharSequence[ 0 ]));
-        String entryValues = PluginUtils.getOrElse(preferenceModel.get("entryValues"), "");
-        preference.setEntryValues(PluginUtils.stringListToListString(entryValues).
-                toArray(new CharSequence[ 0 ]));
-        preference.setDefaultValue(preferenceModel.get("defaultValue"));
-    }
-
-    /**
-     * Sets specific attributes for Preference that have for a base class MultiSelectListPreference
-     * Sets the entries, entryValues and defaultValues
-     *
-     * @param preference      the multi select list preference
-     * @param preferenceModel the map of attributes
-     */
-    private void setMultiSelectListPreferenceAttributes(MultiSelectListPreference preference,
-                                                        Map<String, String> preferenceModel) {
-        setDialogPreferenceAttributes(preference, preferenceModel);
-        String entries = PluginUtils.getOrElse(preferenceModel.get("entries"), "[]");
-        preference.setEntries(PluginUtils.stringListToListString(entries).
-                toArray(new CharSequence[ 0 ]));
-        String entryValues = PluginUtils.getOrElse(preferenceModel.get("entryValues"), "[]");
-        preference.setEntryValues(PluginUtils.stringListToListString(entryValues).
-                toArray(new CharSequence[ 0 ]));
-        String defaultValues = PluginUtils.getOrElse(preferenceModel.get("defaultValue"), "[]");
-        preference.setEntryValues(PluginUtils.stringListToListString(entryValues).
-                toArray(new CharSequence[ 0 ]));
-        Set<CharSequence> set = new HashSet<>(PluginUtils.stringListToListString(defaultValues));
-        preference.setDefaultValue(set);
-    }
-
-    /**
-     * Sets specific attributes for setSeekBarPreference
-     *
-     * @param preference      the seek bar preference
-     * @param preferenceModel the map of attributes
-     */
-    private void setSeekBarPreferenceAttributes(SeekBarPreference preference,
-                                                Map<String, String> preferenceModel) {
-        int min = 0, max = 1, increment = 1;
-        int defaultValue = 0;
-        try {
-            min = Integer.parseInt(PluginUtils.getOrElse(preferenceModel.get("min"), "0"));
-            max = Integer.parseInt(PluginUtils.getOrElse(preferenceModel.get("max"), "1"));
-            increment = Integer.parseInt(PluginUtils.getOrElse(preferenceModel.get("increment"), "1"));
-            defaultValue = Integer.parseInt(PluginUtils.getOrElse(preferenceModel.get("defaultValue"), "[0"));
-        } catch (NumberFormatException e) {
-            Log.e(TAG, e.toString());
-        }
-        preference.setMin(min);
-        preference.setMax(max);
-        preference.setSeekBarIncrement(increment);
-        preference.setAdjustable(Boolean.parseBoolean(PluginUtils.getOrElse(preferenceModel.get("adjustable"), "true")));
-        preference.setDefaultValue(defaultValue);
-        preference.setShowSeekBarValue(true);
-        preference.setUpdatesContinuously(true);
-    }
-
-    /**
-     * Sets specific attributes for twoStatePreference like Switch and CheckBox
-     *
-     * @param preference      the two state preference
-     * @param preferenceModel the map of attributes
-     */
-    private void setTwoStatePreferenceAttributes(TwoStatePreference preference,
-                                                 Map<String, String> preferenceModel) {
-        preference.setChecked(ppds.getString("always", "1").equals("1"));
-    }
-}
diff --git a/ring-android/app/src/main/java/cx/ring/settings/pluginssettings/PluginSettingsFragment.kt b/ring-android/app/src/main/java/cx/ring/settings/pluginssettings/PluginSettingsFragment.kt
new file mode 100644
index 000000000..8267c85dd
--- /dev/null
+++ b/ring-android/app/src/main/java/cx/ring/settings/pluginssettings/PluginSettingsFragment.kt
@@ -0,0 +1,313 @@
+package cx.ring.settings.pluginssettings
+
+import android.content.DialogInterface
+import android.os.Bundle
+import android.util.Log
+import android.view.*
+import androidx.preference.*
+import com.google.android.material.dialog.MaterialAlertDialogBuilder
+import cx.ring.R
+import cx.ring.client.HomeActivity
+import cx.ring.plugins.PluginPreferences
+import cx.ring.plugins.PluginUtils.stringListToListString
+import net.jami.daemon.JamiService
+
+class PluginSettingsFragment : PreferenceFragmentCompat() {
+    private var mPreferencesAttributes: List<Map<String, String>>? = null
+    private var pluginDetails: PluginDetails? = null
+    private var ppds: PluginPreferencesDataStore? = null
+    override fun onCreatePreferences(savedInstanceState: Bundle, rootKey: String) {}
+    override fun onCreate(savedInstanceState: Bundle?) {
+        super.onCreate(savedInstanceState)
+        val arguments = requireArguments()
+        val details = PluginDetails(arguments.getString("name")!!, arguments.getString("rootPath")!!, arguments.getBoolean("enabled"))
+        mPreferencesAttributes = details.pluginPreferences
+        val preferenceManager = preferenceManager
+        ppds = PluginPreferencesDataStore(details)
+        pluginDetails = details
+        preferenceManager.preferenceDataStore = ppds
+        setHasOptionsMenu(true)
+    }
+
+    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
+        val root = super.onCreateView(inflater, container, savedInstanceState)
+        val screen = preferenceManager.createPreferenceScreen(requireContext())
+        screen.addPreference(createHeadPreference())
+        for (preference in createPreferences(mPreferencesAttributes)) {
+            screen.addPreference(preference)
+        }
+        preferenceScreen = screen
+        return root!!
+    }
+
+    override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
+        inflater.inflate(R.menu.plugin_edition, menu)
+        val item = menu.findItem(R.id.menuitem_delete)
+        item.isVisible = false
+        super.onCreateOptionsMenu(menu, inflater)
+    }
+
+    /**
+     * Takes a list of preferences attributes map
+     * Creates a preference View for each map of attributes in the preference
+     *
+     * @param preferencesAttributes A list of preferences attributes
+     * @return list of preferences
+     */
+    private fun createPreferences(preferencesAttributes: List<Map<String, String>>?): List<Preference> {
+        val preferencesViews: MutableList<Preference> = ArrayList()
+        if (preferencesAttributes != null) {
+            for (preferenceAttributes in preferencesAttributes) {
+                val type = preferenceAttributes["type"]
+                // Call for each type the appropriate function member
+                if (type != null) {
+                    when (type) {
+                        "CheckBox" -> preferencesViews.add(createCheckBoxPreference(preferenceAttributes))
+                        "DropDown" -> preferencesViews.add(createDropDownPreference(preferenceAttributes))
+                        "EditText" -> preferencesViews.add(createEditTextPreference(preferenceAttributes))
+                        "List" -> preferencesViews.add(createListPreference(preferenceAttributes))
+                        "Path" -> preferencesViews.add(createPathPreference(preferenceAttributes))
+                        "MultiSelectList" -> preferencesViews.add(createMultiSelectListPreference(preferenceAttributes))
+                        "SeekBar" -> preferencesViews.add(createSeekBarPreference(preferenceAttributes))
+                        "Switch" -> preferencesViews.add(createSwitchPreference(preferenceAttributes))
+                        else -> {
+                        }
+                    }
+                }
+            }
+        }
+        return preferencesViews
+    }
+
+    private fun createHeadPreference(): Preference {
+        val preference = PluginPreferences(requireContext(), pluginDetails)
+        preference.setResetClickListener {
+            MaterialAlertDialogBuilder(requireContext())
+                .setTitle(preference.title)
+                .setMessage(R.string.plugin_reset_preferences_ask)
+                .setPositiveButton(android.R.string.ok) { dialog: DialogInterface?, id: Int ->
+                    JamiService.resetPluginPreferencesValues(pluginDetails!!.rootPath)
+                    (requireActivity() as HomeActivity).popFragmentImmediate()
+                }
+                .setNegativeButton(android.R.string.cancel) { dialog: DialogInterface?, whichButton: Int -> }
+                .show()
+        }
+        preference.setInstallClickListener {
+            MaterialAlertDialogBuilder(requireContext())
+                .setMessage(R.string.account_delete_dialog_message)
+                .setTitle(R.string.plugin_uninstall_title)
+                .setPositiveButton(android.R.string.ok) { dialog: DialogInterface?, whichButton: Int ->
+                    pluginDetails!!.isEnabled = false
+                    JamiService.uninstallPlugin(pluginDetails!!.rootPath)
+                    (requireActivity() as HomeActivity).popFragmentImmediate()
+                }
+                .setNegativeButton(android.R.string.cancel, null)
+                .show()
+        }
+        return preference
+    }
+
+    private fun createCheckBoxPreference(preferenceModel: Map<String, String>): CheckBoxPreference {
+        val preference = CheckBoxPreference(requireContext())
+        setPreferenceAttributes(preference, preferenceModel)
+        setTwoStatePreferenceAttributes(preference, preferenceModel)
+        ppds!!.addToPreferenceTypes(preferenceModel)
+        return preference
+    }
+
+    private fun createDropDownPreference(preferenceModel: Map<String, String>): DropDownPreference {
+        val preference = DropDownPreference(requireContext())
+        setPreferenceAttributes(preference, preferenceModel)
+        setListPreferenceAttributes(preference, preferenceModel)
+        ppds!!.addToPreferenceTypes(preferenceModel)
+        return preference
+    }
+
+    private fun createEditTextPreference(preferenceModel: Map<String, String>): EditTextPreference {
+        val preference = EditTextPreference(requireContext())
+        setPreferenceAttributes(preference, preferenceModel)
+        setDialogPreferenceAttributes(preference, preferenceModel)
+        setEditTextAttributes(preference, preferenceModel)
+        ppds!!.addToPreferenceTypes(preferenceModel)
+        return preference
+    }
+
+    private fun createListPreference(preferenceModel: Map<String, String>): ListPreference {
+        val preference = ListPreference(requireContext())
+        setPreferenceAttributes(preference, preferenceModel)
+        setListPreferenceAttributes(preference, preferenceModel)
+        ppds!!.addToPreferenceTypes(preferenceModel)
+        return preference
+    }
+
+    private fun createPathPreference(preferenceModel: Map<String, String>): Preference {
+        val preference = Preference(requireContext())
+        preference.onPreferenceClickListener = Preference.OnPreferenceClickListener {
+            (requireActivity() as HomeActivity).gotToPluginPathPreference(pluginDetails!!, preferenceModel["key"]!!)
+            false
+        }
+        setPreferenceAttributes(preference, preferenceModel)
+        ppds!!.addToPreferenceTypes(preferenceModel)
+        return preference
+    }
+
+    private fun createMultiSelectListPreference(preferenceModel: Map<String, String>): MultiSelectListPreference {
+        val preference = MultiSelectListPreference(requireContext())
+        setPreferenceAttributes(preference, preferenceModel)
+        setMultiSelectListPreferenceAttributes(preference, preferenceModel)
+        return preference
+    }
+
+    private fun createSwitchPreference(preferenceModel: Map<String, String>): SwitchPreference {
+        val preference = SwitchPreference(requireContext())
+        setPreferenceAttributes(preference, preferenceModel)
+        setTwoStatePreferenceAttributes(preference, preferenceModel)
+        return preference
+    }
+
+    private fun createSeekBarPreference(preferenceModel: Map<String, String>): SeekBarPreference {
+        val preference = SeekBarPreference(requireContext())
+        setPreferenceAttributes(preference, preferenceModel)
+        setSeekBarPreferenceAttributes(preference, preferenceModel)
+        return preference
+    }
+
+    /**
+     * Sets attributes that are common in the Preference base class
+     *
+     * @param preference      the preference
+     * @param preferenceModel the map of attributes
+     */
+    private fun setPreferenceAttributes(preference: Preference, preferenceModel: Map<String, String>) {
+        val key = preferenceModel["key"]
+        preference.key = key
+        preference.title = preferenceModel["title"] ?: ""
+        preference.summary = preferenceModel["summary"] ?: ""
+    }
+
+    //TODO : add drawable icon
+    private fun setDialogPreferenceAttributes(preference: DialogPreference, preferenceModel: Map<String, String>) {
+        val dialogTitle = preferenceModel["dialogTitle"] ?: ""
+        val dialogMessage = preferenceModel["dialogMessage"] ?: ""
+        val positiveButtonText = preferenceModel["positiveButtonText"] ?: ""
+        val negativeButtonText = preferenceModel["negativeButtonText"] ?: ""
+        if (dialogTitle.isNotEmpty()) {
+            preference.dialogTitle = dialogTitle
+        }
+        if (dialogMessage.isNotEmpty()) {
+            preference.dialogTitle = dialogMessage
+        }
+        if (positiveButtonText.isNotEmpty()) {
+            preference.positiveButtonText = positiveButtonText
+        }
+        if (negativeButtonText.isNotEmpty()) {
+            preference.negativeButtonText = negativeButtonText
+        }
+    }
+
+    /**
+     * Sets attributes specific to EditTextPreference
+     * Here we set the default value
+     *
+     * @param preference      EditTextPreference
+     * @param preferenceModel the map of attributes
+     */
+    private fun setEditTextAttributes(
+        preference: EditTextPreference,
+        preferenceModel: Map<String, String>
+    ) {
+        preference.setDefaultValue(preferenceModel["defaultValue"])
+    }
+
+    /**
+     * Sets specific attributes for Preference that have for a base class ListPreference
+     * Sets the entries, entryValues and defaultValue
+     *
+     * @param preference      the list preference
+     * @param preferenceModel the map of attributes
+     */
+    private fun setListPreferenceAttributes(preference: ListPreference, preferenceModel: Map<String, String>) {
+        setDialogPreferenceAttributes(preference, preferenceModel)
+        val entries = preferenceModel["entries"] ?: ""
+        preference.entries = entries.split(',').toTypedArray<CharSequence>()
+        val entryValues = preferenceModel["entryValues"] ?: ""
+        preference.entryValues = stringListToListString(entryValues).toTypedArray<CharSequence>()
+        preference.setDefaultValue(preferenceModel["defaultValue"])
+    }
+
+    /**
+     * Sets specific attributes for Preference that have for a base class MultiSelectListPreference
+     * Sets the entries, entryValues and defaultValues
+     *
+     * @param preference      the multi select list preference
+     * @param preferenceModel the map of attributes
+     */
+    private fun setMultiSelectListPreferenceAttributes(
+        preference: MultiSelectListPreference,
+        preferenceModel: Map<String, String>
+    ) {
+        setDialogPreferenceAttributes(preference, preferenceModel)
+        val entries = preferenceModel["entries"] ?: "[]"
+        preference.entries = stringListToListString(entries).toTypedArray<CharSequence>()
+        val entryValues = preferenceModel["entryValues"] ?: "[]"
+        preference.entryValues = stringListToListString(entryValues).toTypedArray<CharSequence>()
+        val defaultValues = preferenceModel["defaultValue"] ?: "[]"
+        preference.entryValues = stringListToListString(entryValues).toTypedArray<CharSequence>()
+        val set: Set<CharSequence> = HashSet<CharSequence>(stringListToListString(defaultValues))
+        preference.setDefaultValue(set)
+    }
+
+    /**
+     * Sets specific attributes for setSeekBarPreference
+     *
+     * @param preference      the seek bar preference
+     * @param preferenceModel the map of attributes
+     */
+    private fun setSeekBarPreferenceAttributes(
+        preference: SeekBarPreference,
+        preferenceModel: Map<String, String>
+    ) {
+        var min = 0
+        var max = 1
+        var increment = 1
+        var defaultValue = 0
+        try {
+            min = (preferenceModel["min"] ?: "0").toInt()
+            max = (preferenceModel["max"]  ?: "1").toInt()
+            increment = (preferenceModel["increment"] ?: "1").toInt()
+            defaultValue = (preferenceModel["defaultValue"] ?: "[0").toInt()
+        } catch (e: NumberFormatException) {
+            Log.e(TAG, e.toString())
+        }
+        preference.min = min
+        preference.max = max
+        preference.seekBarIncrement = increment
+        preference.isAdjustable = (preferenceModel["adjustable"] ?: "true").toBoolean()
+        preference.setDefaultValue(defaultValue)
+        preference.showSeekBarValue = true
+        preference.updatesContinuously = true
+    }
+
+    /**
+     * Sets specific attributes for twoStatePreference like Switch and CheckBox
+     *
+     * @param preference      the two state preference
+     * @param preferenceModel the map of attributes
+     */
+    private fun setTwoStatePreferenceAttributes(preference: TwoStatePreference, preferenceModel: Map<String, String>) {
+        preference.isChecked = ppds!!.getString("always", "1") == "1"
+    }
+
+    companion object {
+        val TAG = PluginSettingsFragment::class.simpleName!!
+        fun newInstance(pluginDetails: PluginDetails): PluginSettingsFragment {
+            val psf = PluginSettingsFragment()
+            psf.arguments = Bundle().apply {
+                putString("name", pluginDetails.name)
+                putString("rootPath", pluginDetails.rootPath)
+                putBoolean("enabled", pluginDetails.isEnabled)
+            }
+            return psf
+        }
+    }
+}
\ No newline at end of file
diff --git a/ring-android/app/src/main/java/cx/ring/settings/pluginssettings/PluginsListAdapter.java b/ring-android/app/src/main/java/cx/ring/settings/pluginssettings/PluginsListAdapter.java
deleted file mode 100644
index 6d41be74d..000000000
--- a/ring-android/app/src/main/java/cx/ring/settings/pluginssettings/PluginsListAdapter.java
+++ /dev/null
@@ -1,99 +0,0 @@
-package cx.ring.settings.pluginssettings;
-
-import android.graphics.drawable.Drawable;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.CheckBox;
-import android.widget.ImageView;
-import android.widget.TextView;
-
-import androidx.annotation.NonNull;
-import androidx.recyclerview.widget.RecyclerView;
-
-import java.util.List;
-
-import cx.ring.R;
-
-public class PluginsListAdapter extends RecyclerView.Adapter<PluginsListAdapter.PluginViewHolder> {
-    private List<PluginDetails> mList;
-    private final PluginListItemListener listener;
-    public static final String TAG = PluginsListAdapter.class.getSimpleName();
-
-    public PluginsListAdapter(List<PluginDetails> pluginsList, PluginListItemListener listener) {
-        this.mList = pluginsList;
-        this.listener = listener;
-    }
-
-    @NonNull
-    @Override
-    public PluginViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
-        View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.frag_plugins_list_item, parent, false);
-        return new PluginViewHolder(view, listener);
-    }
-
-    @Override
-    public void onBindViewHolder(@NonNull PluginViewHolder holder, int position) {
-        holder.setDetails(mList.get(position));
-    }
-
-    @Override
-    public int getItemCount() {
-        return mList.size();
-    }
-
-    public void updatePluginsList(List<PluginDetails> listPlugins) {
-        mList = listPlugins;
-        notifyDataSetChanged();
-    }
-
-    static class PluginViewHolder extends RecyclerView.ViewHolder{
-        private final ImageView pluginIcon;
-        private final TextView pluginNameTextView;
-        private final CheckBox pluginItemEnableCheckbox;
-        private PluginDetails details = null;
-
-        PluginViewHolder(@NonNull View itemView, PluginListItemListener listener) {
-            super(itemView);
-            // Views that should be updated by the update method
-            pluginIcon = itemView.findViewById(R.id.plugin_item_icon);
-            pluginNameTextView = itemView.findViewById(R.id.plugin_item_name);
-            pluginItemEnableCheckbox = itemView.findViewById(R.id.plugin_item_enable_checkbox);
-
-            // Set listeners, we set the listeners on creation so details can be null
-            itemView.setOnClickListener(v -> {
-                if (details != null) {
-                    listener.onPluginItemClicked(details);
-                }
-            });
-
-            pluginItemEnableCheckbox.setOnClickListener(v -> {
-                if (details != null) {
-                    details.setEnabled(!details.isEnabled());
-                    listener.onPluginEnabled(details);
-                }
-            });
-        }
-
-        public void setDetails(PluginDetails details) {
-            this.details = details;
-            update(this.details);
-        }
-
-        // update the viewHolder view
-        public void update(PluginDetails details) {
-            pluginNameTextView.setText(details.getName());
-            pluginItemEnableCheckbox.setChecked(details.isEnabled());
-            // Set the plugin icon
-            Drawable icon = details.getIcon();
-            if (icon != null) {
-                pluginIcon.setImageDrawable(icon);
-            }
-        }
-    }
-
-    public interface PluginListItemListener {
-        void onPluginItemClicked(PluginDetails pluginDetails);
-        void onPluginEnabled(PluginDetails pluginDetails);
-    }
-}
diff --git a/ring-android/app/src/main/java/cx/ring/settings/pluginssettings/PluginsListAdapter.kt b/ring-android/app/src/main/java/cx/ring/settings/pluginssettings/PluginsListAdapter.kt
new file mode 100644
index 000000000..07fa3c9ec
--- /dev/null
+++ b/ring-android/app/src/main/java/cx/ring/settings/pluginssettings/PluginsListAdapter.kt
@@ -0,0 +1,75 @@
+package cx.ring.settings.pluginssettings
+
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import android.widget.CheckBox
+import android.widget.ImageView
+import android.widget.TextView
+import androidx.recyclerview.widget.RecyclerView
+import cx.ring.R
+import cx.ring.settings.pluginssettings.PluginsListAdapter.PluginViewHolder
+
+class PluginsListAdapter(private var mList: List<PluginDetails>, private val listener: PluginListItemListener) :
+    RecyclerView.Adapter<PluginViewHolder>() {
+    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): PluginViewHolder {
+        val view = LayoutInflater.from(parent.context).inflate(R.layout.frag_plugins_list_item, parent, false)
+        return PluginViewHolder(view, listener)
+    }
+
+    override fun onBindViewHolder(holder: PluginViewHolder, position: Int) {
+        holder.setDetails(mList[position])
+    }
+
+    override fun getItemCount(): Int {
+        return mList.size
+    }
+
+    fun updatePluginsList(listPlugins: List<PluginDetails>) {
+        mList = listPlugins
+        notifyDataSetChanged()
+    }
+
+    class PluginViewHolder(itemView: View, listener: PluginListItemListener) : RecyclerView.ViewHolder(itemView) {
+        private val pluginIcon: ImageView = itemView.findViewById(R.id.plugin_item_icon)
+        private val pluginNameTextView: TextView = itemView.findViewById(R.id.plugin_item_name)
+        private val pluginItemEnableCheckbox: CheckBox = itemView.findViewById(R.id.plugin_item_enable_checkbox)
+        private var details: PluginDetails? = null
+        fun setDetails(details: PluginDetails) {
+            this.details = details
+            update(details)
+        }
+
+        // update the viewHolder view
+        fun update(details: PluginDetails) {
+            pluginNameTextView.text = details.name
+            pluginItemEnableCheckbox.isChecked = details.isEnabled
+            // Set the plugin icon
+            val icon = details.icon
+            if (icon != null) {
+                pluginIcon.setImageDrawable(icon)
+            }
+        }
+
+        init {
+            itemView.setOnClickListener {
+                details?.let { details -> listener.onPluginItemClicked(details) }
+            }
+            pluginItemEnableCheckbox.setOnClickListener {
+                details?.let { details ->
+                    details.isEnabled = !details.isEnabled
+                    listener.onPluginEnabled(details)
+                }
+            }
+        }
+    }
+
+    interface PluginListItemListener {
+        fun onPluginItemClicked(pluginDetails: PluginDetails)
+        fun onPluginEnabled(pluginDetails: PluginDetails)
+    }
+
+    companion object {
+        val TAG = PluginsListAdapter::class.simpleName!!
+    }
+}
\ No newline at end of file
diff --git a/ring-android/app/src/main/java/cx/ring/settings/pluginssettings/PluginsListSettingsFragment.java b/ring-android/app/src/main/java/cx/ring/settings/pluginssettings/PluginsListSettingsFragment.java
deleted file mode 100644
index 0c503ca28..000000000
--- a/ring-android/app/src/main/java/cx/ring/settings/pluginssettings/PluginsListSettingsFragment.java
+++ /dev/null
@@ -1,194 +0,0 @@
-package cx.ring.settings.pluginssettings;
-
-import android.app.Activity;
-import android.content.Context;
-import android.content.Intent;
-import android.net.Uri;
-import android.os.Bundle;
-import android.util.Log;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.Toast;
-
-import androidx.activity.OnBackPressedCallback;
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.fragment.app.Fragment;
-
-import com.google.android.material.snackbar.Snackbar;
-
-import net.jami.daemon.JamiService;
-
-import java.io.File;
-import java.io.IOException;
-import java.util.List;
-
-import cx.ring.R;
-import cx.ring.account.JamiAccountSummaryFragment;
-import cx.ring.client.HomeActivity;
-import cx.ring.databinding.FragPluginsListSettingsBinding;
-import cx.ring.plugins.PluginUtils;
-import cx.ring.utils.AndroidFileUtils;
-import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
-import io.reactivex.rxjava3.disposables.CompositeDisposable;
-
-public class PluginsListSettingsFragment extends Fragment implements PluginsListAdapter.PluginListItemListener {
-
-    public static final String TAG = PluginsListSettingsFragment.class.getSimpleName();
-
-    private final OnBackPressedCallback mOnBackPressedCallback = new OnBackPressedCallback(false) {
-        @Override
-        public void handleOnBackPressed() {
-            mOnBackPressedCallback.setEnabled(false);
-            JamiAccountSummaryFragment fragment = (JamiAccountSummaryFragment) getParentFragment();
-            if (fragment != null) {
-                fragment.popBackStack();
-            }
-        }
-    };
-
-    private static final int ARCHIVE_REQUEST_CODE = 42;
-
-    private FragPluginsListSettingsBinding binding;
-    private PluginsListAdapter mAdapter;
-    private final CompositeDisposable mCompositeDisposable = new CompositeDisposable();
-
-    @Nullable
-    @Override
-    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
-                             @Nullable Bundle savedInstanceState) {
-        binding = FragPluginsListSettingsBinding.inflate(inflater, container, false);
-
-        // use this setting to improve performance if you know that changes
-        // in content do not change the layout size of the RecyclerView
-        binding.pluginsList.setHasFixedSize(true);
-
-        // specify an adapter (see also next example)
-        mAdapter = new PluginsListAdapter(PluginUtils.getInstalledPlugins(binding.pluginsList.getContext()), this);
-        binding.pluginsList.setAdapter(mAdapter);
-
-        //Fab
-        binding.pluginsListSettingsFab.setOnClickListener(view -> {
-            Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
-            intent.addCategory(Intent.CATEGORY_OPENABLE);
-            intent.setType("*/*");
-            startActivityForResult(intent, ARCHIVE_REQUEST_CODE);
-        });
-
-        return binding.getRoot();
-    }
-
-    @Override
-    public void onResume() {
-        mOnBackPressedCallback.setEnabled(true);
-        super.onResume();
-    }
-
-    @Override
-    public void onAttach(@NonNull Context context) {
-        super.onAttach(context);
-        requireActivity().getOnBackPressedDispatcher().addCallback(this, mOnBackPressedCallback);
-    }
-
-    /**
-     * Implements PluginListItemListener.onPluginItemClicked which is called when we click on
-     * a plugin list item
-     * @param pluginDetails instance of a plugin details that is sent to PluginSettingsFragment
-     */
-    @Override
-    public void onPluginItemClicked(PluginDetails pluginDetails) {
-        ((HomeActivity) requireActivity()).gotToPluginSettings(pluginDetails);
-    }
-
-    /**
-     * Implements PluginListItemListener.onPluginEnabled which is called when the checkbox
-     * associated with the plugin list item is called
-     * @param pluginDetails instance of a plugin details that is sent to PluginSettingsFragment
-     */
-    @Override
-    public void onPluginEnabled(PluginDetails pluginDetails) {
-
-        if(pluginDetails.isEnabled()) {
-            pluginDetails.setEnabled(PluginUtils.loadPlugin(pluginDetails.getRootPath()));
-        } else {
-            PluginUtils.unloadPlugin(pluginDetails.getRootPath());
-        }
-        String status = pluginDetails.isEnabled()?"ON":"OFF";
-        Toast.makeText(requireContext(), pluginDetails.getName() + " " + status,
-                Toast.LENGTH_SHORT).show();
-    }
-
-    @Override
-    public void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
-        if (requestCode == ARCHIVE_REQUEST_CODE && resultCode == Activity.RESULT_OK) {
-            if(data != null) {
-                Uri uri = data.getData();
-                if (uri != null) {
-                    installPluginFromUri(uri, false);
-                }
-            }
-         }
-    }
-
-    private String installPluginFile(File pluginFile, boolean force) throws IOException{
-        int i = JamiService.installPlugin(pluginFile.getAbsolutePath(), force);
-        if (!pluginFile.delete()) {
-            Log.e(TAG,"Plugin Jpl file in the cache not freed");
-        }
-        switch (i) {
-            case 0:
-                return pluginFile.getName();
-            case 100:
-                throw new IOException(getResources()
-                        .getString(R.string.plugin_same_version_exception,
-                                pluginFile.getName()));
-            case 200:
-                throw new IOException(getResources()
-                        .getString(R.string.plugin_recent_version_exception,
-                                pluginFile.getName()));
-            default:
-                throw new IOException(getResources()
-                        .getString(R.string.plugin_install_failure,
-                                pluginFile.getName()));
-        }
-    }
-
-    private void installPluginFromUri(Uri uri, boolean force) {
-        mCompositeDisposable.add(AndroidFileUtils.getCacheFile(requireContext(), uri)
-                .observeOn(AndroidSchedulers.mainThread())
-                .map(file -> installPluginFile(file, force))
-                .subscribe(filename -> {
-                        String[] plugin = filename.split(".jpl");
-                        List<PluginDetails> availablePlugins = PluginUtils.getInstalledPlugins(requireContext());
-                        for (PluginDetails availablePlugin : availablePlugins) {
-                            if (availablePlugin.getName().equals(plugin[0])) {
-                                availablePlugin.setEnabled(true);
-                                onPluginEnabled(availablePlugin);
-                            }
-                        }
-                        mAdapter.updatePluginsList(PluginUtils.getInstalledPlugins(requireContext()));
-                        Toast.makeText(requireContext(), "Plugin: " + filename + " successfully installed", Toast.LENGTH_LONG).show();
-                        mCompositeDisposable.dispose();
-                    }, e -> {
-                        if (binding != null) {
-                            Snackbar sb = Snackbar.make(binding.listLayout, "" + e.getMessage(), Snackbar.LENGTH_LONG);
-                            sb.setAction(R.string.plugin_force_install, v -> installPluginFromUri(uri, true));
-                            sb.show();
-                        }
-                    }));
-    }
-
-    @Override
-    public void onDestroyView() {
-        binding = null;
-        super.onDestroyView();
-    }
-
-    @Override
-    public void onDestroy() {
-        super.onDestroy();
-        mCompositeDisposable.dispose();
-    }
-}
-
diff --git a/ring-android/app/src/main/java/cx/ring/settings/pluginssettings/PluginsListSettingsFragment.kt b/ring-android/app/src/main/java/cx/ring/settings/pluginssettings/PluginsListSettingsFragment.kt
new file mode 100644
index 000000000..2e69adc15
--- /dev/null
+++ b/ring-android/app/src/main/java/cx/ring/settings/pluginssettings/PluginsListSettingsFragment.kt
@@ -0,0 +1,187 @@
+package cx.ring.settings.pluginssettings
+
+import android.app.Activity
+import android.content.Context
+import android.content.Intent
+import android.net.Uri
+import android.os.Bundle
+import android.util.Log
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import android.widget.Toast
+import androidx.activity.OnBackPressedCallback
+import androidx.fragment.app.Fragment
+import com.google.android.material.snackbar.Snackbar
+import cx.ring.R
+import cx.ring.account.JamiAccountSummaryFragment
+import cx.ring.client.HomeActivity
+import cx.ring.databinding.FragPluginsListSettingsBinding
+import cx.ring.plugins.PluginUtils.getInstalledPlugins
+import cx.ring.plugins.PluginUtils.loadPlugin
+import cx.ring.plugins.PluginUtils.unloadPlugin
+import cx.ring.settings.pluginssettings.PluginsListAdapter.PluginListItemListener
+import cx.ring.utils.AndroidFileUtils.getCacheFile
+import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
+import io.reactivex.rxjava3.disposables.CompositeDisposable
+import net.jami.daemon.JamiService
+import java.io.File
+import java.io.IOException
+
+class PluginsListSettingsFragment : Fragment(), PluginListItemListener {
+    private val mOnBackPressedCallback: OnBackPressedCallback = object : OnBackPressedCallback(false) {
+        override fun handleOnBackPressed() {
+            this.isEnabled = false
+            val fragment = parentFragment as JamiAccountSummaryFragment?
+            fragment?.popBackStack()
+        }
+    }
+    private var binding: FragPluginsListSettingsBinding? = null
+    private var mAdapter: PluginsListAdapter? = null
+    private val mCompositeDisposable = CompositeDisposable()
+    override fun onCreateView(
+        inflater: LayoutInflater, container: ViewGroup?,
+        savedInstanceState: Bundle?
+    ): View? {
+        binding = FragPluginsListSettingsBinding.inflate(inflater, container, false)
+
+        // use this setting to improve performance if you know that changes
+        // in content do not change the layout size of the RecyclerView
+        binding!!.pluginsList.setHasFixedSize(true)
+
+        // specify an adapter (see also next example)
+        mAdapter = PluginsListAdapter(getInstalledPlugins(binding!!.pluginsList.context), this)
+        binding!!.pluginsList.adapter = mAdapter
+
+        //Fab
+        binding!!.pluginsListSettingsFab.setOnClickListener { view: View? ->
+            val intent = Intent(Intent.ACTION_OPEN_DOCUMENT)
+            intent.addCategory(Intent.CATEGORY_OPENABLE)
+            intent.type = "*/*"
+            startActivityForResult(intent, ARCHIVE_REQUEST_CODE)
+        }
+        return binding!!.root
+    }
+
+    override fun onResume() {
+        mOnBackPressedCallback.isEnabled = true
+        super.onResume()
+    }
+
+    override fun onAttach(context: Context) {
+        super.onAttach(context)
+        requireActivity().onBackPressedDispatcher.addCallback(this, mOnBackPressedCallback)
+    }
+
+    /**
+     * Implements PluginListItemListener.onPluginItemClicked which is called when we click on
+     * a plugin list item
+     * @param pluginDetails instance of a plugin details that is sent to PluginSettingsFragment
+     */
+    override fun onPluginItemClicked(pluginDetails: PluginDetails) {
+        (requireActivity() as HomeActivity).gotToPluginSettings(pluginDetails)
+    }
+
+    /**
+     * Implements PluginListItemListener.onPluginEnabled which is called when the checkbox
+     * associated with the plugin list item is called
+     * @param pluginDetails instance of a plugin details that is sent to PluginSettingsFragment
+     */
+    override fun onPluginEnabled(pluginDetails: PluginDetails) {
+        if (pluginDetails.isEnabled) {
+            pluginDetails.isEnabled = loadPlugin(pluginDetails.rootPath)
+        } else {
+            unloadPlugin(pluginDetails.rootPath)
+        }
+        val status = if (pluginDetails.isEnabled) "ON" else "OFF"
+        Toast.makeText(
+            requireContext(), pluginDetails.name + " " + status,
+            Toast.LENGTH_SHORT
+        ).show()
+    }
+
+    override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
+        if (requestCode == ARCHIVE_REQUEST_CODE && resultCode == Activity.RESULT_OK) {
+            if (data != null) {
+                val uri = data.data
+                if (uri != null) {
+                    installPluginFromUri(uri, false)
+                }
+            }
+        }
+    }
+
+    @Throws(IOException::class)
+    private fun installPluginFile(pluginFile: File, force: Boolean): String {
+        val i = JamiService.installPlugin(pluginFile.absolutePath, force)
+        if (!pluginFile.delete()) {
+            Log.e(TAG, "Plugin Jpl file in the cache not freed")
+        }
+        return when (i) {
+            0 -> pluginFile.name
+            100 -> throw IOException(
+                resources
+                    .getString(
+                        R.string.plugin_same_version_exception,
+                        pluginFile.name
+                    )
+            )
+            200 -> throw IOException(
+                resources
+                    .getString(
+                        R.string.plugin_recent_version_exception,
+                        pluginFile.name
+                    )
+            )
+            else -> throw IOException(
+                resources
+                    .getString(
+                        R.string.plugin_install_failure,
+                        pluginFile.name
+                    )
+            )
+        }
+    }
+
+    private fun installPluginFromUri(uri: Uri, force: Boolean) {
+        mCompositeDisposable.add(
+            getCacheFile(requireContext(), uri)
+                .observeOn(AndroidSchedulers.mainThread())
+                .map { file: File -> installPluginFile(file, force) }
+                .subscribe({ filename: String ->
+                    val plugin = filename.split(".jpl".toRegex()).toTypedArray()
+                    val availablePlugins = getInstalledPlugins(requireContext())
+                    for (availablePlugin in availablePlugins) {
+                        if (availablePlugin.name == plugin[0]) {
+                            availablePlugin.isEnabled = true
+                            onPluginEnabled(availablePlugin)
+                        }
+                    }
+                    mAdapter!!.updatePluginsList(getInstalledPlugins(requireContext()))
+                    Toast.makeText(requireContext(), "Plugin: $filename successfully installed", Toast.LENGTH_LONG)
+                        .show()
+                    mCompositeDisposable.dispose()
+                }) { e: Throwable ->
+                    if (binding != null) {
+                        val sb = Snackbar.make(binding!!.listLayout, "" + e.message, Snackbar.LENGTH_LONG)
+                        sb.setAction(R.string.plugin_force_install) { v: View? -> installPluginFromUri(uri, true) }
+                        sb.show()
+                    }
+                })
+    }
+
+    override fun onDestroyView() {
+        binding = null
+        super.onDestroyView()
+    }
+
+    override fun onDestroy() {
+        super.onDestroy()
+        mCompositeDisposable.dispose()
+    }
+
+    companion object {
+        val TAG = PluginsListSettingsFragment::class.java.simpleName
+        private const val ARCHIVE_REQUEST_CODE = 42
+    }
+}
\ No newline at end of file
diff --git a/ring-android/app/src/main/java/cx/ring/share/ShareFragment.java b/ring-android/app/src/main/java/cx/ring/share/ShareFragment.java
deleted file mode 100644
index 997bb7dec..000000000
--- a/ring-android/app/src/main/java/cx/ring/share/ShareFragment.java
+++ /dev/null
@@ -1,103 +0,0 @@
-/*
- *  Copyright (C) 2004-2021 Savoir-faire Linux Inc.
- *
- *  Authors: Adrien Béraud <adrien.beraud@savoirfairelinux.com>
- *           Alexandre Lision <alexandre.lision@savoirfairelinux.com>
- *
- *  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 cx.ring.share;
-
-import android.content.Intent;
-import android.graphics.Bitmap;
-import android.os.Bundle;
-import android.text.TextUtils;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-
-import net.jami.mvp.GenericView;
-import net.jami.share.SharePresenter;
-import net.jami.share.ShareViewModel;
-import net.jami.utils.QRCodeUtils;
-
-import cx.ring.R;
-import cx.ring.databinding.FragShareBinding;
-import cx.ring.mvp.BaseSupportFragment;
-import dagger.hilt.android.AndroidEntryPoint;
-
-@AndroidEntryPoint
-public class ShareFragment extends BaseSupportFragment<SharePresenter, GenericView<ShareViewModel>> implements GenericView<ShareViewModel> {
-
-    private String mUriToShow;
-    private boolean isShareLocked = false;
-    private FragShareBinding binding;
-
-    @Nullable
-    @Override
-    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
-        binding = FragShareBinding.inflate(inflater, container, false);
-        return binding.getRoot();
-    }
-
-    @Override
-    public void onDestroyView() {
-        super.onDestroyView();
-        binding = null;
-    }
-
-    @Override
-    public void onViewCreated(@NonNull View view, Bundle savedInstanceState) {
-        super.onViewCreated(view, savedInstanceState);
-        setHasOptionsMenu(true);
-        binding.shareButton.setOnClickListener(v -> {
-            if (!isShareLocked) shareAccount();
-        });
-    }
-
-    private void shareAccount() {
-        if (!TextUtils.isEmpty(mUriToShow)) {
-            Intent sharingIntent = new Intent(Intent.ACTION_SEND);
-            sharingIntent.setType("text/plain");
-            sharingIntent.putExtra(Intent.EXTRA_SUBJECT, getText(R.string.account_contact_me));
-            sharingIntent.putExtra(Intent.EXTRA_TEXT, getString(R.string.account_share_body, mUriToShow, getText(R.string.app_website)));
-            startActivity(Intent.createChooser(sharingIntent, getText(R.string.share_via)));
-        }
-    }
-
-    @Override
-    public void showViewModel(final ShareViewModel viewModel) {
-        if (binding == null)
-            return;
-
-        final QRCodeUtils.QRCodeData qrCodeData = viewModel.getAccountQRCodeData(
-                getResources().getColor(R.color.color_primary_dark), getResources().getColor(R.color.transparent));
-        if (qrCodeData == null) {
-            binding.qrImage.setVisibility(View.INVISIBLE);
-            binding.shareInstruction.setText(R.string.share_message_no_account);
-        } else {
-            Bitmap bitmap = Bitmap.createBitmap(qrCodeData.getWidth(), qrCodeData.getHeight(), Bitmap.Config.ARGB_8888);
-            bitmap.setPixels(qrCodeData.getData(), 0, qrCodeData.getWidth(), 0, 0, qrCodeData.getWidth(), qrCodeData.getHeight());
-            binding.qrImage.setImageBitmap(bitmap);
-            binding.shareInstruction.setText(R.string.share_message);
-            binding.qrImage.setVisibility(View.VISIBLE);
-        }
-
-        mUriToShow = viewModel.getAccountDisplayUri();
-        isShareLocked = TextUtils.isEmpty(mUriToShow);
-    }
-}
diff --git a/ring-android/app/src/main/java/cx/ring/share/ShareFragment.kt b/ring-android/app/src/main/java/cx/ring/share/ShareFragment.kt
new file mode 100644
index 000000000..dbaf9b3d9
--- /dev/null
+++ b/ring-android/app/src/main/java/cx/ring/share/ShareFragment.kt
@@ -0,0 +1,84 @@
+/*
+ *  Copyright (C) 2004-2021 Savoir-faire Linux Inc.
+ *
+ *  Authors: Adrien Béraud <adrien.beraud@savoirfairelinux.com>
+ *           Alexandre Lision <alexandre.lision@savoirfairelinux.com>
+ *
+ *  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 cx.ring.share
+
+import android.content.Intent
+import android.graphics.Bitmap
+import android.os.Bundle
+import android.text.TextUtils
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import cx.ring.R
+import cx.ring.databinding.FragShareBinding
+import cx.ring.mvp.BaseSupportFragment
+import dagger.hilt.android.AndroidEntryPoint
+import net.jami.mvp.GenericView
+import net.jami.share.SharePresenter
+import net.jami.share.ShareViewModel
+
+@AndroidEntryPoint
+class ShareFragment : BaseSupportFragment<SharePresenter, GenericView<ShareViewModel>>(), GenericView<ShareViewModel> {
+    private var mUriToShow: String? = null
+    private var isShareLocked = false
+    private var binding: FragShareBinding? = null
+    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
+        binding = FragShareBinding.inflate(inflater, container, false)
+        return binding!!.root
+    }
+
+    override fun onDestroyView() {
+        super.onDestroyView()
+        binding = null
+    }
+
+    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+        super.onViewCreated(view, savedInstanceState)
+        setHasOptionsMenu(true)
+        binding!!.shareButton.setOnClickListener { if (!isShareLocked) shareAccount() }
+    }
+
+    private fun shareAccount() {
+        if (!TextUtils.isEmpty(mUriToShow)) {
+            val sharingIntent = Intent(Intent.ACTION_SEND)
+            sharingIntent.type = "text/plain"
+            sharingIntent.putExtra(Intent.EXTRA_SUBJECT, getText(R.string.account_contact_me))
+            sharingIntent.putExtra(Intent.EXTRA_TEXT, getString(R.string.account_share_body, mUriToShow, getText(R.string.app_website)))
+            startActivity(Intent.createChooser(sharingIntent, getText(R.string.share_via)))
+        }
+    }
+
+    override fun showViewModel(viewModel: ShareViewModel) {
+        if (binding == null) return
+        val qrCodeData = viewModel.getAccountQRCodeData(resources.getColor(R.color.color_primary_dark), resources.getColor(R.color.transparent))
+        if (qrCodeData == null) {
+            binding!!.qrImage.visibility = View.INVISIBLE
+            binding!!.shareInstruction.setText(R.string.share_message_no_account)
+        } else {
+            val bitmap = Bitmap.createBitmap(qrCodeData.width, qrCodeData.height, Bitmap.Config.ARGB_8888)
+            bitmap.setPixels(qrCodeData.data, 0, qrCodeData.width, 0, 0, qrCodeData.width, qrCodeData.height)
+            binding!!.qrImage.setImageBitmap(bitmap)
+            binding!!.shareInstruction.setText(R.string.share_message)
+            binding!!.qrImage.visibility = View.VISIBLE
+        }
+        mUriToShow = viewModel.accountDisplayUri
+        isShareLocked = TextUtils.isEmpty(mUriToShow)
+    }
+}
\ No newline at end of file
diff --git a/ring-android/app/src/main/java/cx/ring/tv/about/AboutDetailsFragment.java b/ring-android/app/src/main/java/cx/ring/tv/about/AboutDetailsFragment.java
index 7207eabf5..f07f4646a 100644
--- a/ring-android/app/src/main/java/cx/ring/tv/about/AboutDetailsFragment.java
+++ b/ring-android/app/src/main/java/cx/ring/tv/about/AboutDetailsFragment.java
@@ -30,6 +30,8 @@ import androidx.leanback.widget.FullWidthDetailsOverviewRowPresenter;
 import androidx.leanback.widget.ListRow;
 import androidx.leanback.widget.ListRowPresenter;
 import androidx.leanback.widget.RowPresenter;
+
+import android.util.Log;
 import android.view.View;
 import android.view.ViewGroup;
 
@@ -37,7 +39,6 @@ import cx.ring.R;
 import cx.ring.tv.cards.Card;
 import cx.ring.tv.cards.iconcards.IconCard;
 import cx.ring.tv.cards.iconcards.IconCardHelper;
-import net.jami.utils.Log;
 
 public class AboutDetailsFragment extends DetailsSupportFragment {
     private static final String TAG = "AboutDetailsFragment";
diff --git a/ring-android/app/src/main/java/cx/ring/tv/account/JamiPreferenceFragment.java b/ring-android/app/src/main/java/cx/ring/tv/account/JamiPreferenceFragment.java
index 10d98bce5..352d5a206 100644
--- a/ring-android/app/src/main/java/cx/ring/tv/account/JamiPreferenceFragment.java
+++ b/ring-android/app/src/main/java/cx/ring/tv/account/JamiPreferenceFragment.java
@@ -22,6 +22,7 @@ package cx.ring.tv.account;
 import android.os.Bundle;
 
 import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
 import androidx.leanback.preference.LeanbackPreferenceFragmentCompat;
 
 import android.view.View;
@@ -38,7 +39,7 @@ public abstract class JamiPreferenceFragment<T extends RootPresenter> extends Le
     protected T presenter;
 
     @Override
-    public void onViewCreated(@NonNull View view, Bundle savedInstanceState) {
+    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
         super.onViewCreated(view, savedInstanceState);
 
         //Be sure to do the injection in onCreateView method
diff --git a/ring-android/app/src/main/java/cx/ring/tv/account/TVAccountExport.kt b/ring-android/app/src/main/java/cx/ring/tv/account/TVAccountExport.kt
index f5ecb6583..c585b8e75 100644
--- a/ring-android/app/src/main/java/cx/ring/tv/account/TVAccountExport.kt
+++ b/ring-android/app/src/main/java/cx/ring/tv/account/TVAccountExport.kt
@@ -39,6 +39,7 @@ import dagger.hilt.android.AndroidEntryPoint
 import net.jami.account.JamiAccountSummaryPresenter
 import net.jami.account.JamiAccountSummaryView
 import net.jami.model.Account
+import net.jami.model.Profile
 import java.io.File
 
 @AndroidEntryPoint
@@ -52,7 +53,7 @@ class TVAccountExport : JamiGuidedStepFragment<JamiAccountSummaryPresenter>(), J
         presenter.setAccountId(mIdAccount)
     }
 
-    override fun onCreateGuidance(savedInstanceState: Bundle): Guidance {
+    override fun onCreateGuidance(savedInstanceState: Bundle?): Guidance {
         val title = getString(R.string.account_export_title)
         val breadcrumb = ""
         val description = getString(R.string.account_link_export_info_light)
@@ -60,7 +61,7 @@ class TVAccountExport : JamiGuidedStepFragment<JamiAccountSummaryPresenter>(), J
         return Guidance(title, description, breadcrumb, icon)
     }
 
-    override fun onCreateActions(actions: List<GuidedAction>, savedInstanceState: Bundle) {
+    override fun onCreateActions(actions: List<GuidedAction>, savedInstanceState: Bundle?) {
         if (mHasPassword) {
             addPasswordAction(activity, actions, PASSWORD, getString(R.string.account_enter_password), "", "")
         } else {
@@ -89,7 +90,7 @@ class TVAccountExport : JamiGuidedStepFragment<JamiAccountSummaryPresenter>(), J
     }
 
     override fun showPasswordProgressDialog() {}
-    override fun accountChanged(account: Account) {}
+    override fun accountChanged(account: Account, profile: Profile) {}
     override fun showNetworkError() {
         mWaitDialog!!.dismiss()
         AlertDialog.Builder(activity)
@@ -164,7 +165,7 @@ class TVAccountExport : JamiGuidedStepFragment<JamiAccountSummaryPresenter>(), J
     override fun askCameraPermission() {}
     override fun goToGallery() {}
     override fun askGalleryPermission() {}
-    override fun updateUserView(account: Account) {}
+    override fun updateUserView(account: Account, profile: Profile) {}
     override fun goToMedia(accountId: String) {}
     override fun goToSystem(accountId: String) {}
     override fun goToAdvanced(accountId: String) {}
diff --git a/ring-android/app/src/main/java/cx/ring/tv/account/TVAccountWizard.kt b/ring-android/app/src/main/java/cx/ring/tv/account/TVAccountWizard.kt
index 00a9cb5c3..a46291a09 100644
--- a/ring-android/app/src/main/java/cx/ring/tv/account/TVAccountWizard.kt
+++ b/ring-android/app/src/main/java/cx/ring/tv/account/TVAccountWizard.kt
@@ -28,6 +28,7 @@ import cx.ring.R
 import cx.ring.account.AccountCreationModelImpl
 import cx.ring.application.JamiApplication
 import cx.ring.mvp.BaseActivity
+import cx.ring.services.VCardServiceImpl
 import dagger.hilt.android.AndroidEntryPoint
 import ezvcard.VCard
 import io.reactivex.rxjava3.core.Single
@@ -36,11 +37,11 @@ import net.jami.account.AccountWizardPresenter
 import net.jami.account.AccountWizardView
 import net.jami.model.Account
 import net.jami.model.AccountConfig
-import net.jami.mvp.AccountCreationModel
+import net.jami.model.AccountCreationModel
 import net.jami.utils.VCardUtils
 
 @AndroidEntryPoint
-class TVAccountWizard : BaseActivity<AccountWizardPresenter?>(), AccountWizardView {
+class TVAccountWizard : BaseActivity<AccountWizardPresenter>(), AccountWizardView {
     private var mProgress: ProgressDialog? = null
     private var mLinkAccount = false
     private var mAccountType: String? = null
@@ -57,15 +58,11 @@ class TVAccountWizard : BaseActivity<AccountWizardPresenter?>(), AccountWizardVi
             mAccountType = AccountConfig.ACCOUNT_TYPE_RING
         }
         if (savedInstanceState == null) {
-            GuidedStepSupportFragment.addAsRoot(
-                this,
-                TVHomeAccountCreationFragment(),
-                android.R.id.content
-            )
+            GuidedStepSupportFragment.addAsRoot(this, TVHomeAccountCreationFragment(), android.R.id.content)
         } else {
             mLinkAccount = savedInstanceState.getBoolean("mLinkAccount")
         }
-        presenter!!.init(if (getIntent().action != null) getIntent().action else AccountConfig.ACCOUNT_TYPE_RING)
+        presenter.init(getIntent().action ?: AccountConfig.ACCOUNT_TYPE_RING)
     }
 
     override fun onSaveInstanceState(outState: Bundle) {
@@ -74,8 +71,8 @@ class TVAccountWizard : BaseActivity<AccountWizardPresenter?>(), AccountWizardVi
     }
 
     override fun onDestroy() {
-        if (mProgress != null) {
-            mProgress!!.dismiss()
+        mProgress?.let { progress ->
+            progress.dismiss()
             mProgress = null
         }
         super.onDestroy()
@@ -83,9 +80,9 @@ class TVAccountWizard : BaseActivity<AccountWizardPresenter?>(), AccountWizardVi
 
     fun createAccount(accountCreationModel: AccountCreationModel) {
         if (accountCreationModel.isLink) {
-            presenter!!.initJamiAccountLink(accountCreationModel, getText(R.string.ring_account_default_name).toString())
+            presenter.initJamiAccountLink(accountCreationModel, getText(R.string.ring_account_default_name).toString())
         } else {
-            presenter!!.initJamiAccountCreation(accountCreationModel, getText(R.string.ring_account_default_name).toString())
+            presenter.initJamiAccountCreation(accountCreationModel, getText(R.string.ring_account_default_name).toString())
         }
     }
 
@@ -97,10 +94,7 @@ class TVAccountWizard : BaseActivity<AccountWizardPresenter?>(), AccountWizardVi
     }
 
     override fun goToProfileCreation(accountCreationModel: AccountCreationModel) {
-        GuidedStepSupportFragment.add(
-            supportFragmentManager,
-            TVProfileCreationFragment.newInstance(accountCreationModel as AccountCreationModelImpl)
-        )
+        GuidedStepSupportFragment.add(supportFragmentManager, TVProfileCreationFragment.newInstance(accountCreationModel as AccountCreationModelImpl))
     }
 
     override fun displayProgress(display: Boolean) {
@@ -113,10 +107,9 @@ class TVAccountWizard : BaseActivity<AccountWizardPresenter?>(), AccountWizardVi
                 show()
             }
         } else {
-            if (mProgress != null) {
-                if (mProgress!!.isShowing) {
-                    mProgress!!.dismiss()
-                }
+            mProgress?.let { progress ->
+                if (progress.isShowing)
+                    progress.dismiss()
                 mProgress = null
             }
         }
@@ -147,7 +140,7 @@ class TVAccountWizard : BaseActivity<AccountWizardPresenter?>(), AccountWizardVi
         val filedir = filesDir
         return accountCreationModel.toVCard()
             .flatMap { vcard ->
-                account.resetProfile()
+                account.loadedProfile = Single.fromCallable { VCardServiceImpl.readData(vcard) }.cache()
                 VCardUtils.saveLocalProfileToDisk(vcard, account.accountID, filedir)
             }
             .subscribeOn(Schedulers.io())
@@ -195,7 +188,7 @@ class TVAccountWizard : BaseActivity<AccountWizardPresenter?>(), AccountWizardVi
         finish()
     }
 
-    fun profileCreated(accountCreationModel: AccountCreationModel?, saveProfile: Boolean) {
-        presenter!!.profileCreated(accountCreationModel, saveProfile)
+    fun profileCreated(accountCreationModel: AccountCreationModel, saveProfile: Boolean) {
+        presenter.profileCreated(accountCreationModel, saveProfile)
     }
 }
\ No newline at end of file
diff --git a/ring-android/app/src/main/java/cx/ring/tv/account/TVJamiAccountCreationFragment.java b/ring-android/app/src/main/java/cx/ring/tv/account/TVJamiAccountCreationFragment.java
index 7b1bd5c57..947751514 100644
--- a/ring-android/app/src/main/java/cx/ring/tv/account/TVJamiAccountCreationFragment.java
+++ b/ring-android/app/src/main/java/cx/ring/tv/account/TVJamiAccountCreationFragment.java
@@ -25,6 +25,7 @@ import androidx.leanback.widget.GuidanceStylist;
 import androidx.leanback.widget.GuidedAction;
 import android.text.Editable;
 import android.text.TextWatcher;
+import android.util.Log;
 import android.view.View;
 import android.view.ViewGroup;
 import android.widget.EditText;
@@ -35,11 +36,9 @@ import cx.ring.R;
 import cx.ring.account.AccountCreationModelImpl;
 import net.jami.account.JamiAccountCreationPresenter;
 import net.jami.account.JamiAccountCreationView;
-import cx.ring.application.JamiApplication;
 import dagger.hilt.android.AndroidEntryPoint;
 
-import net.jami.mvp.AccountCreationModel;
-import net.jami.utils.Log;
+import net.jami.model.AccountCreationModel;
 import net.jami.utils.StringUtils;
 
 @AndroidEntryPoint
diff --git a/ring-android/app/src/main/java/cx/ring/tv/account/TVJamiLinkAccountFragment.java b/ring-android/app/src/main/java/cx/ring/tv/account/TVJamiLinkAccountFragment.java
index 5ff65dcc1..6bdbebc68 100644
--- a/ring-android/app/src/main/java/cx/ring/tv/account/TVJamiLinkAccountFragment.java
+++ b/ring-android/app/src/main/java/cx/ring/tv/account/TVJamiLinkAccountFragment.java
@@ -18,6 +18,7 @@
 package cx.ring.tv.account;
 
 import android.app.Activity;
+import android.graphics.Bitmap;
 import android.graphics.drawable.Drawable;
 import android.os.Bundle;
 import androidx.annotation.NonNull;
@@ -31,10 +32,10 @@ import cx.ring.R;
 import cx.ring.account.AccountCreationModelImpl;
 import net.jami.account.JamiLinkAccountPresenter;
 import net.jami.account.JamiLinkAccountView;
-import cx.ring.application.JamiApplication;
+
 import dagger.hilt.android.AndroidEntryPoint;
 
-import net.jami.mvp.AccountCreationModel;
+import net.jami.model.AccountCreationModel;
 import net.jami.utils.StringUtils;
 
 @AndroidEntryPoint
@@ -59,7 +60,7 @@ public class TVJamiLinkAccountFragment extends JamiGuidedStepFragment<JamiLinkAc
         super.onViewCreated(view, savedInstanceState);
         presenter.init(model);
         if (model != null && model.getPhoto() != null) {
-            getGuidanceStylist().getIconView().setImageBitmap(model.getPhoto());
+            getGuidanceStylist().getIconView().setImageBitmap((Bitmap) model.getPhoto());
         }
     }
 
@@ -105,7 +106,7 @@ public class TVJamiLinkAccountFragment extends JamiGuidedStepFragment<JamiLinkAc
     }
 
     @Override
-    public void createAccount(AccountCreationModel accountCreationModel) {
+    public void createAccount(@NonNull AccountCreationModel accountCreationModel) {
         ((TVAccountWizard) getActivity()).createAccount(accountCreationModel);
     }
 
diff --git a/ring-android/app/src/main/java/cx/ring/tv/account/TVProfileCreationFragment.kt b/ring-android/app/src/main/java/cx/ring/tv/account/TVProfileCreationFragment.kt
index 4d1e32a33..080c5faf7 100644
--- a/ring-android/app/src/main/java/cx/ring/tv/account/TVProfileCreationFragment.kt
+++ b/ring-android/app/src/main/java/cx/ring/tv/account/TVProfileCreationFragment.kt
@@ -45,7 +45,7 @@ import dagger.hilt.android.AndroidEntryPoint
 import io.reactivex.rxjava3.core.Single
 import net.jami.account.ProfileCreationPresenter
 import net.jami.account.ProfileCreationView
-import net.jami.mvp.AccountCreationModel
+import net.jami.model.AccountCreationModel
 
 @AndroidEntryPoint
 class TVProfileCreationFragment : JamiGuidedStepFragment<ProfileCreationPresenter>(),
@@ -59,14 +59,14 @@ class TVProfileCreationFragment : JamiGuidedStepFragment<ProfileCreationPresente
                 try {
                     requireContext().contentResolver.openInputStream(uri!!).use { iStream ->
                         val image = BitmapFactory.decodeStream(iStream)
-                        presenter!!.photoUpdated(Single.just(intent).map { image })
+                        presenter.photoUpdated(Single.just(intent).map { image })
                     }
                 } catch (e: Exception) {
                     e.printStackTrace()
                 }
             }
             ProfileCreationFragment.REQUEST_CODE_GALLERY -> if (resultCode == Activity.RESULT_OK && intent != null) {
-                presenter!!.photoUpdated(
+                presenter.photoUpdated(
                     loadBitmap(requireContext(), intent.data!!).map { b: Bitmap? -> b })
             }
             else -> super.onActivityResult(requestCode, resultCode, intent)
@@ -76,11 +76,11 @@ class TVProfileCreationFragment : JamiGuidedStepFragment<ProfileCreationPresente
     override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<String>, grantResults: IntArray) {
         when (requestCode) {
             ProfileCreationFragment.REQUEST_PERMISSION_CAMERA -> if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
-                presenter!!.cameraPermissionChanged(true)
-                presenter!!.cameraClick()
+                presenter.cameraPermissionChanged(true)
+                presenter.cameraClick()
             }
             ProfileCreationFragment.REQUEST_PERMISSION_READ_STORAGE -> if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
-                presenter!!.galleryClick()
+                presenter.galleryClick()
             }
         }
     }
@@ -92,10 +92,10 @@ class TVProfileCreationFragment : JamiGuidedStepFragment<ProfileCreationPresente
             return
         }
         iconSize = resources.getDimension(R.dimen.tv_avatar_size).toInt()
-        presenter!!.initPresenter(mModel)
+        presenter.initPresenter(mModel!!)
     }
 
-    override fun onCreateGuidance(savedInstanceState: Bundle): Guidance {
+    override fun onCreateGuidance(savedInstanceState: Bundle?): Guidance {
         val title = getString(R.string.account_create_title)
         val breadcrumb = ""
         val description = getString(R.string.profile_message_warning)
@@ -113,7 +113,7 @@ class TVProfileCreationFragment : JamiGuidedStepFragment<ProfileCreationPresente
         return R.style.Theme_Ring_Leanback_GuidedStep_First
     }
 
-    override fun onCreateActions(actions: List<GuidedAction>, savedInstanceState: Bundle) {
+    override fun onCreateActions(actions: List<GuidedAction>, savedInstanceState: Bundle?) {
         addEditTextAction(activity, actions, USER_NAME.toLong(), R.string.profile_name_hint, R.string.profile_name_hint)
         addAction(activity, actions, CAMERA.toLong(), getString(R.string.take_a_photo), "")
         addAction(activity, actions, GALLERY.toLong(), getString(R.string.open_the_gallery), "")
@@ -122,9 +122,9 @@ class TVProfileCreationFragment : JamiGuidedStepFragment<ProfileCreationPresente
 
     override fun onGuidedActionClicked(action: GuidedAction) {
         when (action.id) {
-            CAMERA.toLong() -> presenter!!.cameraClick()
-            GALLERY.toLong() -> presenter!!.galleryClick()
-            NEXT.toLong() -> presenter!!.nextClick()
+            CAMERA.toLong() -> presenter.cameraClick()
+            GALLERY.toLong() -> presenter.galleryClick()
+            NEXT.toLong() -> presenter.nextClick()
         }
     }
 
@@ -176,8 +176,8 @@ class TVProfileCreationFragment : JamiGuidedStepFragment<ProfileCreationPresente
         val model = accountCreationModel as AccountCreationModelImpl
         val newAccount = model.newAccount
         val avatar = AvatarDrawable.Builder()
-            .withPhoto(model.photo)
-            .withNameData(accountCreationModel.getFullName(), accountCreationModel.getUsername())
+            .withPhoto(model.photo as Bitmap?)
+            .withNameData(accountCreationModel.fullName, accountCreationModel.username)
             .withId(newAccount?.username)
             .withCircleCrop(true)
             .build(requireContext())
@@ -189,12 +189,12 @@ class TVProfileCreationFragment : JamiGuidedStepFragment<ProfileCreationPresente
         when (action.id.toInt()) {
             USER_NAME -> {
                 val username = action.editTitle.toString()
-                presenter!!.fullNameUpdated(username)
+                presenter.fullNameUpdated(username)
                 if (username.isEmpty()) action.title =
                     getString(R.string.profile_name_hint) else action.title = username
             }
-            CAMERA -> presenter!!.cameraClick()
-            GALLERY -> presenter!!.galleryClick()
+            CAMERA -> presenter.cameraClick()
+            GALLERY -> presenter.galleryClick()
         }
         return super.onGuidedActionEditedAndProceed(action)
     }
@@ -202,7 +202,7 @@ class TVProfileCreationFragment : JamiGuidedStepFragment<ProfileCreationPresente
     override fun onGuidedActionEditCanceled(action: GuidedAction) {
         if (action.id.toInt() == USER_NAME) {
             val username = action.editTitle.toString()
-            presenter!!.fullNameUpdated(username)
+            presenter.fullNameUpdated(username)
             if (TextUtils.isEmpty(username)) action.title =
                 getString(R.string.profile_name_hint) else action.title = username
         }
diff --git a/ring-android/app/src/main/java/cx/ring/tv/account/TVProfileEditingFragment.kt b/ring-android/app/src/main/java/cx/ring/tv/account/TVProfileEditingFragment.kt
index 7042d907a..d6c129613 100644
--- a/ring-android/app/src/main/java/cx/ring/tv/account/TVProfileEditingFragment.kt
+++ b/ring-android/app/src/main/java/cx/ring/tv/account/TVProfileEditingFragment.kt
@@ -25,6 +25,7 @@ import android.content.Intent
 import android.content.pm.PackageManager
 import android.graphics.Bitmap
 import android.graphics.BitmapFactory
+import android.graphics.drawable.Drawable
 import android.net.Uri
 import android.os.Bundle
 import android.provider.MediaStore
@@ -36,10 +37,9 @@ import androidx.leanback.widget.GuidedAction
 import cx.ring.R
 import cx.ring.account.ProfileCreationFragment
 import cx.ring.tv.camera.CustomCameraActivity
-import cx.ring.utils.AndroidFileUtils.loadBitmap
+import cx.ring.utils.AndroidFileUtils
 import cx.ring.utils.BitmapUtils
 import cx.ring.views.AvatarDrawable
-import cx.ring.views.AvatarDrawable.Companion.load
 import dagger.hilt.android.AndroidEntryPoint
 import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
 import io.reactivex.rxjava3.core.Single
@@ -69,7 +69,7 @@ class TVProfileEditingFragment : JamiGuidedStepFragment<HomeNavigationPresenter>
                 }
             }
             ProfileCreationFragment.REQUEST_CODE_GALLERY -> if (resultCode == Activity.RESULT_OK && data != null) {
-                presenter.saveVCardPhoto(loadBitmap(requireContext(), data.data!!)
+                presenter.saveVCardPhoto(AndroidFileUtils.loadBitmap(requireContext(), data.data!!)
                     .map { obj: Bitmap -> BitmapUtils.bitmapToPhoto(obj) })
             }
             else -> {
@@ -80,11 +80,11 @@ class TVProfileEditingFragment : JamiGuidedStepFragment<HomeNavigationPresenter>
     override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<String>, grantResults: IntArray) {
         when (requestCode) {
             ProfileCreationFragment.REQUEST_PERMISSION_CAMERA -> if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
-                presenter!!.cameraPermissionChanged(true)
-                presenter!!.cameraClicked()
+                presenter.cameraPermissionChanged(true)
+                presenter.cameraClicked()
             }
             ProfileCreationFragment.REQUEST_PERMISSION_READ_STORAGE -> if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
-                presenter!!.galleryClicked()
+                presenter.galleryClicked()
             }
         }
     }
@@ -131,31 +131,23 @@ class TVProfileEditingFragment : JamiGuidedStepFragment<HomeNavigationPresenter>
 
     override fun showViewModel(viewModel: HomeNavigationViewModel) {
         val action = actions?.let { if (it.isEmpty()) null else it[0] }
-        if (action != null && action.id == USER_NAME.toLong()) {
-            if (TextUtils.isEmpty(viewModel.alias)) {
+        if (action != null && action.id == USER_NAME) {
+            if (TextUtils.isEmpty(viewModel.profile.displayName)) {
                 action.editTitle = ""
                 action.title = getString(R.string.account_edit_profile)
             } else {
-                action.editTitle = viewModel.alias
-                action.title = viewModel.alias
+                action.editTitle = viewModel.profile.displayName
+                action.title = viewModel.profile.displayName
             }
             notifyActionChanged(0)
         }
-        if (TextUtils.isEmpty(viewModel.alias))
+        if (TextUtils.isEmpty(viewModel.profile.displayName))
             guidanceStylist.titleView.setText(R.string.profile)
-        else guidanceStylist.titleView.text = viewModel.alias
-        setPhoto(viewModel.account)
+        else guidanceStylist.titleView.text = viewModel.profile.displayName
+        guidanceStylist.iconView.setImageDrawable(AvatarDrawable.build(requireContext(), viewModel.account, viewModel.profile, true)
+            .apply { setInSize(iconSize) })
     }
 
-    override fun setPhoto(account: Account) {
-        load(requireContext(), account)
-            .map { avatar -> avatar.apply { setInSize(iconSize) } }
-            .subscribeOn(Schedulers.io())
-            .observeOn(AndroidSchedulers.mainThread())
-            .subscribe { avatar: AvatarDrawable? -> guidanceStylist.iconView.setImageDrawable(avatar) }
-    }
-
-    override fun updateModel(account: Account) {}
     override fun gotToImageCapture() {
         val intent = Intent(activity, CustomCameraActivity::class.java)
         startActivityForResult(intent, ProfileCreationFragment.REQUEST_CODE_PHOTO)
diff --git a/ring-android/app/src/main/java/cx/ring/tv/account/TVShareFragment.kt b/ring-android/app/src/main/java/cx/ring/tv/account/TVShareFragment.kt
index b7b868719..7efb23299 100644
--- a/ring-android/app/src/main/java/cx/ring/tv/account/TVShareFragment.kt
+++ b/ring-android/app/src/main/java/cx/ring/tv/account/TVShareFragment.kt
@@ -21,15 +21,15 @@ package cx.ring.tv.account
 
 import android.graphics.Bitmap
 import android.os.Bundle
+import android.util.Log
 import android.view.LayoutInflater
 import android.view.View
 import android.view.ViewGroup
 import cx.ring.R
 import cx.ring.databinding.TvFragShareBinding
 import cx.ring.mvp.BaseSupportFragment
-import cx.ring.services.VCardServiceImpl.Companion.loadProfile
+import cx.ring.services.VCardServiceImpl
 import cx.ring.views.AvatarDrawable
-import cx.ring.views.AvatarDrawable.Companion.build
 import dagger.hilt.android.AndroidEntryPoint
 import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
 import io.reactivex.rxjava3.disposables.CompositeDisposable
@@ -37,7 +37,6 @@ import net.jami.model.Account
 import net.jami.mvp.GenericView
 import net.jami.share.SharePresenter
 import net.jami.share.ShareViewModel
-import net.jami.utils.Log
 
 @AndroidEntryPoint
 class TVShareFragment : BaseSupportFragment<SharePresenter, GenericView<ShareViewModel>>(), GenericView<ShareViewModel> {
@@ -77,7 +76,7 @@ class TVShareFragment : BaseSupportFragment<SharePresenter, GenericView<ShareVie
     }
 
     private fun getUserAvatar(account: Account) {
-        disposable.add(loadProfile(requireContext(), account)
+        disposable.add(VCardServiceImpl.loadProfile(requireContext(), account)
             .observeOn(AndroidSchedulers.mainThread())
             .doOnNext {
                 binding?.apply {
@@ -85,12 +84,12 @@ class TVShareFragment : BaseSupportFragment<SharePresenter, GenericView<ShareVie
                     shareUri?.text = account.displayUsername
                 }
             }
-            .map { p -> build(requireContext(), account, p, true) }
-            .subscribe({ a: AvatarDrawable? ->
+            .map { p -> AvatarDrawable.build(requireContext(), account, p, true) }
+            .subscribe({ a: AvatarDrawable ->
                 binding?.apply {
                     qrUserPhoto?.visibility = View.VISIBLE
                     qrUserPhoto?.setImageDrawable(a)
                 }
-            }) { e -> Log.e(TVShareFragment::class.simpleName, e.message) })
+            }) { e -> Log.e(TVShareFragment::class.simpleName!!, e.message!!) })
     }
 }
diff --git a/ring-android/app/src/main/java/cx/ring/tv/call/TVCallActivity.kt b/ring-android/app/src/main/java/cx/ring/tv/call/TVCallActivity.kt
index 3887e845d..9c011f9c8 100644
--- a/ring-android/app/src/main/java/cx/ring/tv/call/TVCallActivity.kt
+++ b/ring-android/app/src/main/java/cx/ring/tv/call/TVCallActivity.kt
@@ -26,13 +26,13 @@ import android.os.Build
 import android.view.WindowManager
 import cx.ring.R
 import android.media.AudioManager
+import android.util.Log
 import android.view.KeyEvent
 import net.jami.services.NotificationService
 import net.jami.call.CallView
 import android.view.MotionEvent
 import cx.ring.application.JamiApplication
 import cx.ring.utils.ConversationPath
-import net.jami.utils.Log
 
 @AndroidEntryPoint
 class TVCallActivity : FragmentActivity() {
diff --git a/ring-android/app/src/main/java/cx/ring/tv/call/TVCallFragment.kt b/ring-android/app/src/main/java/cx/ring/tv/call/TVCallFragment.kt
index 5fca0317e..a75ccb56b 100644
--- a/ring-android/app/src/main/java/cx/ring/tv/call/TVCallFragment.kt
+++ b/ring-android/app/src/main/java/cx/ring/tv/call/TVCallFragment.kt
@@ -117,8 +117,8 @@ class TVCallFragment : BaseSupportFragment<CallPresenter, CallView>(), CallView
         args.getString(CallFragment.KEY_ACTION)?.let { action ->
             if (action == CallFragment.ACTION_PLACE_CALL || action == Intent.ACTION_CALL)
                 prepareCall(false)
-            else if (action == CallFragment.ACTION_GET_CALL || action == CallActivity.ACTION_CALL_ACCEPT)
-                presenter.initIncomingCall(args.getString(CallFragment.KEY_CONF_ID)!!, action == CallFragment.ACTION_GET_CALL)
+            else if (action == Intent.ACTION_VIEW || action == CallActivity.ACTION_CALL_ACCEPT)
+                presenter.initIncomingCall(args.getString(CallFragment.KEY_CONF_ID)!!, action == Intent.ACTION_VIEW)
         }
     }
 
@@ -211,17 +211,12 @@ class TVCallFragment : BaseSupportFragment<CallPresenter, CallView>(), CallView
         if (mScreenWakeLock != null && mScreenWakeLock!!.isHeld) {
             mScreenWakeLock!!.release()
         }
-        val r = runnable
-        if (r != null) {
+        runnable?.let { r ->
             view?.handler?.removeCallbacks(r)
         }
     }
 
-    override fun onRequestPermissionsResult(
-        requestCode: Int,
-        permissions: Array<String>,
-        grantResults: IntArray
-    ) {
+    override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<String>, grantResults: IntArray) {
         super.onRequestPermissionsResult(requestCode, permissions, grantResults)
         if (requestCode != REQUEST_PERMISSION_INCOMING && requestCode != REQUEST_PERMISSION_OUTGOING) return
         var i = 0
@@ -267,14 +262,9 @@ class TVCallFragment : BaseSupportFragment<CallPresenter, CallView>(), CallView
         binding!!.contactBubbleLayout.visibility = if (display) View.VISIBLE else View.GONE
     }
 
-    override fun displayVideoSurface(
-        displayVideoSurface: Boolean,
-        displayPreviewContainer: Boolean
-    ) {
-        binding!!.videoSurface.visibility =
-            if (displayVideoSurface) View.VISIBLE else View.GONE
-        binding!!.previewContainer.visibility =
-            if (displayPreviewContainer) View.VISIBLE else View.GONE
+    override fun displayVideoSurface(displayVideoSurface: Boolean, displayPreviewContainer: Boolean) {
+        binding!!.videoSurface.visibility = if (displayVideoSurface) View.VISIBLE else View.GONE
+        binding!!.previewContainer.visibility = if (displayPreviewContainer) View.VISIBLE else View.GONE
     }
 
     override fun displayPreviewSurface(display: Boolean) {
diff --git a/ring-android/app/src/main/java/cx/ring/tv/cards/contacts/ContactCard.java b/ring-android/app/src/main/java/cx/ring/tv/cards/contacts/ContactCard.java
deleted file mode 100644
index af459b173..000000000
--- a/ring-android/app/src/main/java/cx/ring/tv/cards/contacts/ContactCard.java
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- *  Copyright (C) 2004-2021 Savoir-faire Linux Inc.
- *
- *  Author: Loïc Siret <loic.siret@savoirfairelinux.com>
- *
- *  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, write to the Free Software
- *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-package cx.ring.tv.cards.contacts;
-
-import net.jami.model.Contact;
-import net.jami.smartlist.SmartListViewModel;
-import cx.ring.tv.cards.Card;
-
-public class ContactCard extends Card {
-    private SmartListViewModel mModel;
-
-    public ContactCard(String accountId, Contact pContact, Type type) {
-        mModel =  new SmartListViewModel(accountId, pContact, null);
-        setId(pContact.getId());
-        setTitle(pContact.getDisplayName());
-        setDescription(pContact.getRingUsername());
-        setType(type);
-    }
-
-    public ContactCard(SmartListViewModel model) {
-        setModel(model);
-    }
-
-    public void setModel(SmartListViewModel model) {
-        mModel = model;
-        setTitle(mModel.getContactName());
-        String username = mModel.getContacts().get(0).getRingUsername();
-        setDescription(username);
-        boolean isOnline = mModel.getContacts().get(0).isOnline();
-        if (mModel.getContactName().equals(username)) {
-            if (isOnline) {
-                setType(Type.CONTACT_ONLINE);
-            } else {
-                setType(Type.CONTACT);
-            }
-        } else {
-            if (isOnline) {
-                setType(Type.CONTACT_WITH_USERNAME_ONLINE);
-            } else {
-                setType(Type.CONTACT_WITH_USERNAME);
-            }
-        }
-    }
-
-    public SmartListViewModel getModel() {
-        return mModel;
-    }
-}
diff --git a/ring-android/app/src/main/java/cx/ring/tv/cards/contacts/ContactCard.kt b/ring-android/app/src/main/java/cx/ring/tv/cards/contacts/ContactCard.kt
new file mode 100644
index 000000000..aa1aa0f22
--- /dev/null
+++ b/ring-android/app/src/main/java/cx/ring/tv/cards/contacts/ContactCard.kt
@@ -0,0 +1,65 @@
+/*
+ *  Copyright (C) 2004-2021 Savoir-faire Linux Inc.
+ *
+ *  Author: Loïc Siret <loic.siret@savoirfairelinux.com>
+ *
+ *  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, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+package cx.ring.tv.cards.contacts
+
+import cx.ring.tv.cards.Card
+import net.jami.model.Contact
+import net.jami.smartlist.SmartListViewModel
+
+class ContactCard : Card {
+    private var mModel: SmartListViewModel
+
+    constructor(accountId: String, pContact: Contact, type: Type?) {
+        mModel = SmartListViewModel(accountId, pContact, null)
+        id = pContact.id
+        title = pContact.displayName
+        description = pContact.ringUsername
+        setType(type)
+    }
+
+    constructor(m: SmartListViewModel) {
+        mModel = m
+        model = m
+    }
+
+    var model: SmartListViewModel
+        get() = mModel
+        set(model) {
+            mModel = model
+            title = model.contactName
+            val contact = model.getContact()!!
+            val username = contact.ringUsername
+            description = username
+            val isOnline = contact.isOnline
+            type = if (model.contactName == username) {
+                if (isOnline) {
+                    Type.CONTACT_ONLINE
+                } else {
+                    Type.CONTACT
+                }
+            } else {
+                if (isOnline) {
+                    Type.CONTACT_WITH_USERNAME_ONLINE
+                } else {
+                    Type.CONTACT_WITH_USERNAME
+                }
+            }
+        }
+}
\ No newline at end of file
diff --git a/ring-android/app/src/main/java/cx/ring/tv/cards/contacts/ContactCardPresenter.java b/ring-android/app/src/main/java/cx/ring/tv/cards/contacts/ContactCardPresenter.java
deleted file mode 100644
index c0d4e0336..000000000
--- a/ring-android/app/src/main/java/cx/ring/tv/cards/contacts/ContactCardPresenter.java
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- *  Copyright (C) 2004-2021 Savoir-faire Linux Inc.
- *
- *  Author: Loïc Siret <loic.siret@savoirfairelinux.com>
- *
- *  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, write to the Free Software
- *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-package cx.ring.tv.cards.contacts;
-
-import android.content.Context;
-
-import android.view.ContextThemeWrapper;
-
-import net.jami.smartlist.SmartListViewModel;
-
-import cx.ring.R;
-import cx.ring.tv.cards.AbstractCardPresenter;
-import cx.ring.tv.cards.Card;
-import cx.ring.tv.cards.CardView;
-import cx.ring.views.AvatarDrawable;
-
-public class ContactCardPresenter extends AbstractCardPresenter<CardView> {
-
-    private static final String TAG = ContactCardPresenter.class.getSimpleName();
-
-    public ContactCardPresenter(Context context, int resId) {
-        super(new ContextThemeWrapper(context, resId));
-    }
-
-    @Override
-    protected CardView onCreateView() {
-        return new CardView(getContext());
-    }
-
-    @Override
-    public void onBindViewHolder(Card card, CardView cardView) {
-        ContactCard contact = (ContactCard) card;
-
-        SmartListViewModel model = contact.getModel();
-        cardView.setTitleText(card.getTitle());
-        cardView.setContentText(card.getDescription());
-        cardView.setTitleSingleLine(true);
-        cardView.setBackgroundColor(getContext().getResources().getColor(R.color.tv_transparent));
-        cardView.setInfoAreaBackgroundColor(getContext().getResources().getColor(R.color.transparent));
-
-        cardView.setMainImage(
-                new AvatarDrawable.Builder()
-                        .withViewModel(model)
-                        .withPresence(false)
-                        .withCircleCrop(false)
-                        .build(getContext())
-        );
-    }
-}
diff --git a/ring-android/app/src/main/java/cx/ring/tv/cards/contacts/ContactCardPresenter.kt b/ring-android/app/src/main/java/cx/ring/tv/cards/contacts/ContactCardPresenter.kt
new file mode 100644
index 000000000..775df9204
--- /dev/null
+++ b/ring-android/app/src/main/java/cx/ring/tv/cards/contacts/ContactCardPresenter.kt
@@ -0,0 +1,49 @@
+/*
+ *  Copyright (C) 2004-2021 Savoir-faire Linux Inc.
+ *
+ *  Author: Loïc Siret <loic.siret@savoirfairelinux.com>
+ *
+ *  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, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+package cx.ring.tv.cards.contacts
+
+import android.content.Context
+import android.view.ContextThemeWrapper
+import cx.ring.R
+import cx.ring.tv.cards.AbstractCardPresenter
+import cx.ring.tv.cards.Card
+import cx.ring.tv.cards.CardView
+import cx.ring.views.AvatarDrawable
+
+class ContactCardPresenter(context: Context, resId: Int) :
+    AbstractCardPresenter<CardView>(ContextThemeWrapper(context, resId)) {
+    override fun onCreateView(): CardView {
+        return CardView(context)
+    }
+
+    override fun onBindViewHolder(card: Card, cardView: CardView) {
+        val contact = card as ContactCard
+        cardView.titleText = card.getTitle()
+        cardView.contentText = card.getDescription()
+        cardView.setTitleSingleLine(true)
+        cardView.setBackgroundColor(context.resources.getColor(R.color.tv_transparent))
+        cardView.setInfoAreaBackgroundColor(context.resources.getColor(R.color.transparent))
+        cardView.mainImage = AvatarDrawable.Builder()
+            .withViewModel(contact.model)
+            .withPresence(false)
+            .withCircleCrop(false)
+            .build(context)
+    }
+}
\ No newline at end of file
diff --git a/ring-android/app/src/main/java/cx/ring/tv/contact/TVContactFragment.kt b/ring-android/app/src/main/java/cx/ring/tv/contact/TVContactFragment.kt
index af1757d9f..fb929dd52 100644
--- a/ring-android/app/src/main/java/cx/ring/tv/contact/TVContactFragment.kt
+++ b/ring-android/app/src/main/java/cx/ring/tv/contact/TVContactFragment.kt
@@ -68,26 +68,18 @@ class TVContactFragment : BaseDetailFragment<TVContactPresenter>(), TVContactVie
         setupAdapter()
         val res = resources
         iconSize = res.getDimensionPixelSize(R.dimen.tv_avatar_size)
-        presenter!!.setContact(mConversationPath)
+        presenter.setContact(mConversationPath)
     }
 
     private fun setupAdapter() {
         // Set detail background and style.
         val detailsPresenter: FullWidthDetailsOverviewRowPresenter = if (isIncomingRequest || isOutgoingRequest) {
-            FullWidthDetailsOverviewRowPresenter(
-                TVContactRequestDetailPresenter(),
-                DetailsOverviewLogoPresenter()
-            )
+            FullWidthDetailsOverviewRowPresenter(TVContactRequestDetailPresenter(),DetailsOverviewLogoPresenter())
         } else {
-            FullWidthDetailsOverviewRowPresenter(
-                TVContactDetailPresenter(),
-                DetailsOverviewLogoPresenter()
-            )
+            FullWidthDetailsOverviewRowPresenter(TVContactDetailPresenter(), DetailsOverviewLogoPresenter())
         }
-        detailsPresenter.backgroundColor =
-            ContextCompat.getColor(requireContext(), R.color.tv_contact_background)
-        detailsPresenter.actionsBackgroundColor =
-            ContextCompat.getColor(requireContext(), R.color.tv_contact_row_background)
+        detailsPresenter.backgroundColor = ContextCompat.getColor(requireContext(), R.color.tv_contact_background)
+        detailsPresenter.actionsBackgroundColor = ContextCompat.getColor(requireContext(), R.color.tv_contact_row_background)
         detailsPresenter.initialState = FullWidthDetailsOverviewRowPresenter.STATE_HALF
 
         // Hook up transition element.
@@ -101,24 +93,18 @@ class TVContactFragment : BaseDetailFragment<TVContactPresenter>(), TVContactVie
         }
         detailsPresenter.onActionClickedListener = OnActionClickedListener { action: Action ->
             if (action.id == ACTION_CALL.toLong()) {
-                presenter!!.contactClicked()
+                presenter.contactClicked()
             } else if (action.id == ACTION_ADD_CONTACT.toLong()) {
-                presenter!!.onAddContact()
+                presenter.onAddContact()
             } else if (action.id == ACTION_ACCEPT.toLong()) {
-                presenter!!.acceptTrustRequest()
+                presenter.acceptTrustRequest()
             } else if (action.id == ACTION_REFUSE.toLong()) {
-                presenter!!.refuseTrustRequest()
+                presenter.refuseTrustRequest()
             } else if (action.id == ACTION_BLOCK.toLong()) {
-                presenter!!.blockTrustRequest()
+                presenter.blockTrustRequest()
             } else if (action.id == ACTION_MORE.toLong()) {
-                startActivityForResult(
-                    Intent(getActivity(), TVContactMoreActivity::class.java)
-                        .setDataAndType(
-                            mConversationPath!!.toUri(),
-                            TVContactMoreActivity.CONTACT_REQUEST_URI
-                        ),
-                    REQUEST_CODE
-                )
+                startActivityForResult(Intent(getActivity(), TVContactMoreActivity::class.java)
+                    .setDataAndType(mConversationPath.toUri(), TVContactMoreActivity.CONTACT_REQUEST_URI), REQUEST_CODE)
             }
         }
         val mPresenterSelector = ClassPresenterSelector()
@@ -161,30 +147,25 @@ class TVContactFragment : BaseDetailFragment<TVContactPresenter>(), TVContactVie
     }
 
     override fun callContact(accountId: String, conversationUri: Uri, uri: Uri) {
-        startActivity(
-            Intent(Intent.ACTION_CALL)
-                .setClass(requireContext(), TVCallActivity::class.java)
-                .putExtras(ConversationPath.toBundle(accountId, conversationUri))
-                .putExtra(Intent.EXTRA_PHONE_NUMBER, uri.uri)
-        )
+        startActivity(Intent(Intent.ACTION_CALL)
+            .setClass(requireContext(), TVCallActivity::class.java)
+            .putExtras(ConversationPath.toBundle(accountId, conversationUri))
+            .putExtra(Intent.EXTRA_PHONE_NUMBER, uri.uri))
     }
 
     override fun goToCallActivity(id: String) {
-        startActivity(
-            Intent(requireContext(), TVCallActivity::class.java)
-                .putExtra(NotificationService.KEY_CALL_ID, id)
-        )
+        startActivity(Intent(requireContext(), TVCallActivity::class.java)
+            .putExtra(NotificationService.KEY_CALL_ID, id))
     }
 
     override fun switchToConversationView() {
         isIncomingRequest = false
         isOutgoingRequest = false
         setupAdapter()
-        presenter!!.setContact(mConversationPath)
+        presenter.setContact(mConversationPath)
     }
 
     override fun finishView() {
-        val activity: Activity? = activity
         activity?.onBackPressed()
     }
 
@@ -194,7 +175,6 @@ class TVContactFragment : BaseDetailFragment<TVContactPresenter>(), TVContactVie
     }
 
     companion object {
-        @JvmField
         val TAG = TVContactFragment::class.simpleName!!
         private const val ACTION_CALL = 0
         private const val ACTION_ACCEPT = 1
diff --git a/ring-android/app/src/main/java/cx/ring/tv/contact/TVContactPresenter.kt b/ring-android/app/src/main/java/cx/ring/tv/contact/TVContactPresenter.kt
index dea907483..5e2f9db48 100644
--- a/ring-android/app/src/main/java/cx/ring/tv/contact/TVContactPresenter.kt
+++ b/ring-android/app/src/main/java/cx/ring/tv/contact/TVContactPresenter.kt
@@ -53,7 +53,7 @@ class TVContactPresenter @Inject constructor(
             .getAccountSubject(path.accountId)
             .map { a: Account -> SmartListViewModel(a.getByUri(mUri)!!, true) }
             .observeOn(mUiScheduler)
-            .subscribe { c: SmartListViewModel -> view!!.showContact(c) })
+            .subscribe { c: SmartListViewModel -> view?.showContact(c) })
     }
 
     fun contactClicked() {
diff --git a/ring-android/app/src/main/java/cx/ring/tv/contact/TVContactView.java b/ring-android/app/src/main/java/cx/ring/tv/contact/TVContactView.java
index d891d3bc0..5de1f8604 100644
--- a/ring-android/app/src/main/java/cx/ring/tv/contact/TVContactView.java
+++ b/ring-android/app/src/main/java/cx/ring/tv/contact/TVContactView.java
@@ -21,10 +21,9 @@
 package cx.ring.tv.contact;
 
 import net.jami.model.Uri;
-import net.jami.mvp.BaseView;
 import net.jami.smartlist.SmartListViewModel;
 
-public interface TVContactView extends BaseView {
+public interface TVContactView {
     void showContact(SmartListViewModel model);
 
     void callContact(String accountID, Uri conversationUri, Uri uri);
diff --git a/ring-android/app/src/main/java/cx/ring/tv/contact/more/TVContactMoreActivity.java b/ring-android/app/src/main/java/cx/ring/tv/contact/more/TVContactMoreActivity.kt
similarity index 64%
rename from ring-android/app/src/main/java/cx/ring/tv/contact/more/TVContactMoreActivity.java
rename to ring-android/app/src/main/java/cx/ring/tv/contact/more/TVContactMoreActivity.kt
index 40e8b2413..85525b730 100644
--- a/ring-android/app/src/main/java/cx/ring/tv/contact/more/TVContactMoreActivity.java
+++ b/ring-android/app/src/main/java/cx/ring/tv/contact/more/TVContactMoreActivity.kt
@@ -17,24 +17,21 @@
  * along with this program; if not, write to the Free Software
  *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
+package cx.ring.tv.contact.more
 
-package cx.ring.tv.contact.more;
-
-import android.os.Bundle;
-
-import androidx.fragment.app.FragmentActivity;
-
-import cx.ring.R;
-import dagger.hilt.android.AndroidEntryPoint;
+import dagger.hilt.android.AndroidEntryPoint
+import androidx.fragment.app.FragmentActivity
+import android.os.Bundle
+import cx.ring.R
 
 @AndroidEntryPoint
-public class TVContactMoreActivity extends FragmentActivity {
-
-    public static final String CONTACT_REQUEST_URI = "uri";
+class TVContactMoreActivity : FragmentActivity() {
+    override fun onCreate(savedInstanceState: Bundle?) {
+        super.onCreate(savedInstanceState)
+        setContentView(R.layout.tv_activity_contact_more)
+    }
 
-    @Override
-    protected void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-        setContentView(R.layout.tv_activity_contact_more);
+    companion object {
+        const val CONTACT_REQUEST_URI = "uri"
     }
-}
+}
\ No newline at end of file
diff --git a/ring-android/app/src/main/java/cx/ring/tv/contact/more/TVContactMoreFragment.kt b/ring-android/app/src/main/java/cx/ring/tv/contact/more/TVContactMoreFragment.kt
index e8996373d..cde26a27d 100644
--- a/ring-android/app/src/main/java/cx/ring/tv/contact/more/TVContactMoreFragment.kt
+++ b/ring-android/app/src/main/java/cx/ring/tv/contact/more/TVContactMoreFragment.kt
@@ -40,29 +40,23 @@ class TVContactMoreFragment : LeanbackSettingsFragmentCompat() {
         startPreferenceFragment(PrefsFragment.newInstance())
     }
 
-    override fun onPreferenceStartFragment(
-        preferenceFragment: PreferenceFragmentCompat,
-        preference: Preference
-    ): Boolean {
+    override fun onPreferenceStartFragment(preferenceFragment: PreferenceFragmentCompat, preference: Preference): Boolean {
         return false
     }
 
-    override fun onPreferenceStartScreen(
-        caller: PreferenceFragmentCompat,
-        pref: PreferenceScreen
-    ): Boolean {
+    override fun onPreferenceStartScreen(caller: PreferenceFragmentCompat, pref: PreferenceScreen): Boolean {
         return false
     }
 
     @AndroidEntryPoint
     class PrefsFragment : JamiPreferenceFragment<TVContactMorePresenter>(), TVContactMoreView {
-        override fun onCreatePreferences(savedInstanceState: Bundle, rootKey: String) {
+        override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
             setPreferencesFromResource(R.xml.tv_contact_more_pref, rootKey)
         }
 
         override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
             super.onViewCreated(view, savedInstanceState)
-            presenter!!.setContact(fromIntent(requireActivity().intent))
+            presenter.setContact(fromIntent(requireActivity().intent))
         }
 
         override fun onPreferenceTreeClick(preference: Preference): Boolean {
@@ -70,12 +64,12 @@ class TVContactMoreFragment : LeanbackSettingsFragmentCompat() {
                 createDialog(
                     getString(R.string.conversation_action_history_clear_title),
                     getString(R.string.clear_history)
-                ) { dialog: DialogInterface?, whichButton: Int -> presenter!!.clearHistory() }
+                ) { dialog: DialogInterface?, whichButton: Int -> presenter.clearHistory() }
             } else if (preference.key == "Contact.delete") {
                 createDialog(
                     getString(R.string.conversation_action_remove_this_title),
                     getString(R.string.menu_delete)
-                ) { dialog: DialogInterface?, whichButton: Int -> presenter!!.removeContact() }
+                ) { dialog: DialogInterface?, whichButton: Int -> presenter.removeContact() }
             }
             return super.onPreferenceTreeClick(preference)
         }
diff --git a/ring-android/app/src/main/java/cx/ring/tv/conversation/TvConversationFragment.kt b/ring-android/app/src/main/java/cx/ring/tv/conversation/TvConversationFragment.kt
index 0ed5ceeb9..d1e9b9273 100644
--- a/ring-android/app/src/main/java/cx/ring/tv/conversation/TvConversationFragment.kt
+++ b/ring-android/app/src/main/java/cx/ring/tv/conversation/TvConversationFragment.kt
@@ -122,21 +122,25 @@ class TvConversationFragment : BaseSupportFragment<ConversationPresenter, Conver
     private fun displaySpeechRecognizer() {
         if (!checkAudioPermission()) return
         try {
-            val intent = Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH)
-                .putExtra(
-                    RecognizerIntent.EXTRA_LANGUAGE_MODEL,
-                    RecognizerIntent.LANGUAGE_MODEL_FREE_FORM
-                )
-                .putExtra(
-                    RecognizerIntent.EXTRA_PROMPT,
-                    getText(R.string.conversation_input_speech_hint)
-                )
-            startActivityForResult(intent, REQUEST_SPEECH_CODE)
+            startActivityForResult(Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH)
+                .putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL, RecognizerIntent.LANGUAGE_MODEL_FREE_FORM)
+                .putExtra(RecognizerIntent.EXTRA_PROMPT, getText(R.string.conversation_input_speech_hint)), REQUEST_SPEECH_CODE)
         } catch (e: Exception) {
             Snackbar.make(requireView(), "Can't get voice input", Snackbar.LENGTH_SHORT).show()
         }
     }
 
+    override fun displayErrorToast(error: Error) {
+        val errorString: String = when (error) {
+            Error.NO_INPUT -> getString(R.string.call_error_no_camera_no_microphone)
+            Error.INVALID_FILE -> getString(R.string.invalid_file)
+            Error.NOT_ABLE_TO_WRITE_FILE -> getString(R.string.not_able_to_write_file)
+            Error.NO_SPACE_LEFT -> getString(R.string.no_space_left_on_device)
+            else -> getString(R.string.generic_error)
+        }
+        Toast.makeText(requireContext(), errorString, Toast.LENGTH_LONG).show()
+    }
+
     override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
         super.onViewCreated(view, savedInstanceState)
 
@@ -557,27 +561,28 @@ class TvConversationFragment : BaseSupportFragment<ConversationPresenter, Conver
     }
 
     override fun displayContact(conversation: Conversation) {
-        val contacts = conversation.contacts
+        val contacts = conversation.contact ?: return
         mCompositeDisposable.clear()
         mCompositeDisposable.add(AvatarFactory.getAvatar(requireContext(), conversation, true)
-            .doOnSuccess { d: Drawable? ->
+            .doOnSuccess { d: Drawable ->
                 mConversationAvatar = d as AvatarDrawable?
-                mParticipantAvatars[contacts[0].primaryNumber] = AvatarDrawable(d!!)
+                mParticipantAvatars[contacts.primaryNumber] = AvatarDrawable(d)
             }
-            .flatMapObservable { d: Drawable? -> contacts[0].updatesSubject }
+            .flatMapObservable { d: Drawable -> contacts.updatesSubject }
             .observeOn(AndroidSchedulers.mainThread())
             .subscribe { c: Contact ->
                 val id = c.ringUsername
                 val displayName = c.displayName
                 if (binding != null) {
                     binding!!.title.text = displayName
-                    if (TextUtils.isEmpty(displayName) || displayName != id) binding!!.subtitle.text =
-                        id else binding!!.subtitle.visibility = View.GONE
+                    if (TextUtils.isEmpty(displayName) || displayName != id)
+                        binding!!.subtitle.text = id
+                    else
+                        binding!!.subtitle.visibility = View.GONE
                 }
                 mConversationAvatar!!.update(c)
-                val uri = contacts[0].primaryNumber
-                val a = mParticipantAvatars[uri]
-                a?.update(c)
+                val uri = contacts.primaryNumber
+                mParticipantAvatars[uri]?.update(c)
                 if (mAdapter != null) mAdapter!!.setPhoto()
             })
     }
@@ -658,38 +663,26 @@ class TvConversationFragment : BaseSupportFragment<ConversationPresenter, Conver
     }
 
     override fun openFilePicker() {}
-    override fun acceptFile(
-        accountId: String,
-        conversationUri: net.jami.model.Uri,
-        transfer: DataTransfer
-    ) {
+    override fun acceptFile(accountId: String, conversationUri: net.jami.model.Uri, transfer: DataTransfer) {
         val cacheDir = requireContext().cacheDir
         val spaceLeft = getSpaceLeft(cacheDir.toString())
         if (spaceLeft == -1L || transfer.totalSize > spaceLeft) {
             presenter.noSpaceLeft()
             return
         }
-        requireActivity().startService(
-            Intent(DRingService.ACTION_FILE_ACCEPT)
-                .setClass(requireContext(), DRingService::class.java)
-                .setData(ConversationPath.toUri(accountId, conversationUri))
-                .putExtra(DRingService.KEY_MESSAGE_ID, transfer.messageId)
-                .putExtra(DRingService.KEY_TRANSFER_ID, transfer.fileId)
-        )
-    }
-
-    override fun refuseFile(
-        accountId: String,
-        conversationUri: net.jami.model.Uri,
-        transfer: DataTransfer
-    ) {
-        requireActivity().startService(
-            Intent(DRingService.ACTION_FILE_CANCEL)
-                .setClass(requireContext(), DRingService::class.java)
-                .setData(ConversationPath.toUri(accountId, conversationUri))
-                .putExtra(DRingService.KEY_MESSAGE_ID, transfer.messageId)
-                .putExtra(DRingService.KEY_TRANSFER_ID, transfer.fileId)
-        )
+        requireActivity().startService(Intent(DRingService.ACTION_FILE_ACCEPT)
+            .setClass(requireContext(), DRingService::class.java)
+            .setData(ConversationPath.toUri(accountId, conversationUri))
+            .putExtra(DRingService.KEY_MESSAGE_ID, transfer.messageId)
+            .putExtra(DRingService.KEY_TRANSFER_ID, transfer.fileId))
+    }
+
+    override fun refuseFile(accountId: String, conversationUri: net.jami.model.Uri, transfer: DataTransfer) {
+        requireActivity().startService(Intent(DRingService.ACTION_FILE_CANCEL)
+            .setClass(requireContext(), DRingService::class.java)
+            .setData(ConversationPath.toUri(accountId, conversationUri))
+            .putExtra(DRingService.KEY_MESSAGE_ID, transfer.messageId)
+            .putExtra(DRingService.KEY_TRANSFER_ID, transfer.fileId))
     }
 
     companion object {
diff --git a/ring-android/app/src/main/java/cx/ring/tv/main/BaseBrowseFragment.java b/ring-android/app/src/main/java/cx/ring/tv/main/BaseBrowseFragment.java
index 55cb16974..e15f99e6b 100644
--- a/ring-android/app/src/main/java/cx/ring/tv/main/BaseBrowseFragment.java
+++ b/ring-android/app/src/main/java/cx/ring/tv/main/BaseBrowseFragment.java
@@ -24,16 +24,12 @@ import android.os.Bundle;
 import androidx.annotation.NonNull;
 import androidx.leanback.app.BrowseSupportFragment;
 import android.view.View;
-import android.widget.Toast;
 
 import javax.inject.Inject;
 
-import cx.ring.R;
-import net.jami.model.Error;
-import net.jami.mvp.BaseView;
 import net.jami.mvp.RootPresenter;
 
-public class BaseBrowseFragment<T extends RootPresenter> extends BrowseSupportFragment implements BaseView {
+public class BaseBrowseFragment<T extends RootPresenter> extends BrowseSupportFragment {
 
     protected static final String TAG = BaseBrowseFragment.class.getSimpleName();
 
@@ -54,20 +50,6 @@ public class BaseBrowseFragment<T extends RootPresenter> extends BrowseSupportFr
         presenter.unbindView();
     }
 
-    public void displayErrorToast(Error error) {
-        String errorString;
-        switch (error) {
-            case NO_INPUT:
-                errorString = getString(R.string.call_error_no_camera_no_microphone);
-                break;
-            default:
-                errorString = getString(R.string.generic_error);
-                break;
-        }
-
-        Toast.makeText(getActivity(), errorString, Toast.LENGTH_LONG).show();
-    }
-
     protected void initPresenter(T presenter) {
 
     }
diff --git a/ring-android/app/src/main/java/cx/ring/tv/main/BaseDetailFragment.java b/ring-android/app/src/main/java/cx/ring/tv/main/BaseDetailFragment.java
index 42d9e45e5..bff84a7f6 100644
--- a/ring-android/app/src/main/java/cx/ring/tv/main/BaseDetailFragment.java
+++ b/ring-android/app/src/main/java/cx/ring/tv/main/BaseDetailFragment.java
@@ -25,13 +25,11 @@ import android.widget.Toast;
 
 import javax.inject.Inject;
 
+import androidx.annotation.NonNull;
 import androidx.leanback.app.DetailsSupportFragment;
-import cx.ring.R;
-import net.jami.model.Error;
-import net.jami.mvp.BaseView;
 import net.jami.mvp.RootPresenter;
 
-public class BaseDetailFragment<T extends RootPresenter> extends DetailsSupportFragment implements BaseView {
+public class BaseDetailFragment<T extends RootPresenter> extends DetailsSupportFragment {
 
     protected static final String TAG = BaseBrowseFragment.class.getSimpleName();
 
@@ -39,7 +37,7 @@ public class BaseDetailFragment<T extends RootPresenter> extends DetailsSupportF
     protected T presenter;
 
     @Override
-    public void onViewCreated(View view, Bundle savedInstanceState) {
+    public void onViewCreated(@NonNull View view, Bundle savedInstanceState) {
         super.onViewCreated(view, savedInstanceState);
 
         //Be sure to do the injection in onCreateView method
@@ -53,20 +51,6 @@ public class BaseDetailFragment<T extends RootPresenter> extends DetailsSupportF
         presenter.unbindView();
     }
 
-    public void displayErrorToast(Error error) {
-        String errorString;
-        switch (error) {
-            case NO_INPUT:
-                errorString = getString(R.string.call_error_no_camera_no_microphone);
-                break;
-            default:
-                errorString = getString(R.string.generic_error);
-                break;
-        }
-
-        Toast.makeText(getActivity(), errorString, Toast.LENGTH_LONG).show();
-    }
-
     protected void initPresenter(T presenter) {
 
     }
diff --git a/ring-android/app/src/main/java/cx/ring/tv/main/HomeActivity.kt b/ring-android/app/src/main/java/cx/ring/tv/main/HomeActivity.kt
index 31543355a..fa90219da 100644
--- a/ring-android/app/src/main/java/cx/ring/tv/main/HomeActivity.kt
+++ b/ring-android/app/src/main/java/cx/ring/tv/main/HomeActivity.kt
@@ -100,7 +100,7 @@ class HomeActivity : FragmentActivity() {
             out = Allocation.createTyped(rs, rgbaType, Allocation.USAGE_SCRIPT)
             blurIntrinsic = ScriptIntrinsicBlur.create(rs, Element.U8_4(rs)).apply {
                 setRadius(BLUR_RADIUS * size.width / 1080)
-                setInput(input)
+                setInput(out)
             }
             mBlurOutputBitmap = Bitmap.createBitmap(size.width, size.height, Bitmap.Config.ARGB_8888)
             mBlurOut = Allocation.createFromBitmap(rs, mBlurOutputBitmap)
@@ -119,7 +119,7 @@ class HomeActivity : FragmentActivity() {
 
     public override fun onCreate(savedInstanceState: Bundle?) {
         super.onCreate(savedInstanceState)
-        JamiApplication.instance!!.startDaemon()
+        JamiApplication.instance?.startDaemon()
         setContentView(R.layout.tv_activity_home)
         mBackgroundManager = BackgroundManager.getInstance(this).apply { attach(window) }
         mPreviewView = findViewById(R.id.previewView)
diff --git a/ring-android/app/src/main/java/cx/ring/tv/main/MainFragment.kt b/ring-android/app/src/main/java/cx/ring/tv/main/MainFragment.kt
index 44eceaf33..3574f6030 100644
--- a/ring-android/app/src/main/java/cx/ring/tv/main/MainFragment.kt
+++ b/ring-android/app/src/main/java/cx/ring/tv/main/MainFragment.kt
@@ -35,7 +35,7 @@ import androidx.tvprovider.media.tv.ChannelLogoUtils
 import androidx.tvprovider.media.tv.PreviewProgram
 import androidx.tvprovider.media.tv.TvContractCompat
 import cx.ring.R
-import cx.ring.services.VCardServiceImpl.Companion.loadProfile
+import cx.ring.services.VCardServiceImpl
 import cx.ring.tv.account.TVAccountExport
 import cx.ring.tv.account.TVProfileEditingFragment
 import cx.ring.tv.account.TVShareActivity
@@ -49,8 +49,8 @@ import cx.ring.tv.contact.TVContactFragment
 import cx.ring.tv.search.SearchActivity
 import cx.ring.tv.settings.TVSettingsActivity
 import cx.ring.tv.views.CustomTitleView
-import cx.ring.utils.AndroidFileUtils.createImageFile
-import cx.ring.utils.BitmapUtils.drawableToBitmap
+import cx.ring.utils.AndroidFileUtils
+import cx.ring.utils.BitmapUtils
 import cx.ring.utils.ContentUriHandler
 import cx.ring.utils.ConversationPath
 import cx.ring.views.AvatarDrawable
@@ -67,15 +67,10 @@ import net.jami.smartlist.SmartListViewModel
 import net.jami.utils.QRCodeUtils
 import java.io.BufferedOutputStream
 import java.io.FileOutputStream
-import java.util.*
-import cx.ring.tv.cards.ShadowRowPresenterSelector
-
-import androidx.leanback.widget.ArrayObjectAdapter
 
 @AndroidEntryPoint
 class MainFragment : BaseBrowseFragment<MainPresenter>(), MainView {
-    //private TVContactFragment mContactFragment;
-    private var mSpinnerFragment: SpinnerFragment? = null
+    private val mSpinnerFragment: SpinnerFragment = SpinnerFragment()
     private var cardRowAdapter: ArrayObjectAdapter? = null
     private var contactRequestRowAdapter: ArrayObjectAdapter? = null
     private var mTitleView: CustomTitleView? = null
@@ -85,6 +80,7 @@ class MainFragment : BaseBrowseFragment<MainPresenter>(), MainView {
     private var accountSettingsRow: ListRow? = null
     private val mDisposable = CompositeDisposable()
     private val mHomeChannelDisposable = CompositeDisposable()
+
     override fun onCreate(savedInstanceState: Bundle?) {
         super.onCreate(savedInstanceState)
         headersState = HEADERS_DISABLED
@@ -93,7 +89,7 @@ class MainFragment : BaseBrowseFragment<MainPresenter>(), MainView {
     override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
         mTitleView = view.findViewById(R.id.browse_title_group)
         super.onViewCreated(view, savedInstanceState)
-        setupUIElements(requireActivity())
+        setupUIElements(requireContext())
     }
 
     override fun onDestroyView() {
@@ -147,11 +143,10 @@ class MainFragment : BaseBrowseFragment<MainPresenter>(), MainView {
 
     override fun showLoading(show: Boolean) {
         if (show) {
-            mSpinnerFragment = SpinnerFragment()
             parentFragmentManager.beginTransaction()
-                .replace(R.id.main_browse_fragment, mSpinnerFragment!!).commitAllowingStateLoss()
+                .replace(R.id.main_browse_fragment, mSpinnerFragment).commitAllowingStateLoss()
         } else {
-            parentFragmentManager.beginTransaction().remove(mSpinnerFragment!!)
+            parentFragmentManager.beginTransaction().remove(mSpinnerFragment)
                 .commitAllowingStateLoss()
         }
     }
@@ -163,7 +158,7 @@ class MainFragment : BaseBrowseFragment<MainPresenter>(), MainView {
     }
 
     override fun showContacts(contacts: List<SmartListViewModel>) {
-        val cards: MutableList<Card?> = ArrayList(contacts.size + 1)
+        val cards: MutableList<Card> = ArrayList(contacts.size + 1)
         cards.add(IconCardHelper.getAddContactCard(requireContext()))
         for (contact in contacts) cards.add(ContactCard(contact))
         cardRowAdapter!!.setItems(cards, null)
@@ -199,7 +194,7 @@ class MainFragment : BaseBrowseFragment<MainPresenter>(), MainView {
             }
             .subscribeOn(Schedulers.io())
             .subscribe({ program -> cr.insert(TvContractCompat.PreviewPrograms.CONTENT_URI, program.toContentValues()) }
-            ) { e: Throwable? -> Log.w(TAG, "Error updating home channel", e) })
+            ) { e: Throwable -> Log.w(TAG, "Error updating home channel", e) })
     }
 
     override fun showContactRequests(contacts: List<SmartListViewModel>) {
@@ -233,17 +228,17 @@ class MainFragment : BaseBrowseFragment<MainPresenter>(), MainView {
         val context = requireContext()
         val address = account.displayUsername
         mDisposable.clear()
-        mDisposable.add(loadProfile(context, account)
-                .observeOn(AndroidSchedulers.mainThread())
-                .doOnNext { profile ->
-                    val name = profile.first
-                    if (name != null && name.isNotEmpty()) {
-                        mTitleView?.setAlias(name)
-                        title = address ?: ""
-                    } else {
-                        mTitleView?.setAlias(address)
-                    }
+        mDisposable.add(VCardServiceImpl.loadProfile(context, account)
+            .observeOn(AndroidSchedulers.mainThread())
+            .doOnNext { profile ->
+                val name = profile.displayName
+                if (name != null && name.isNotEmpty()) {
+                    mTitleView?.setAlias(name)
+                    title = address ?: ""
+                } else {
+                    mTitleView?.setAlias(address)
                 }
+            }
             .map { p -> AvatarDrawable.build(context, account, p, true) }
             .subscribe { a ->
                 mTitleView?.apply {
@@ -252,8 +247,8 @@ class MainFragment : BaseBrowseFragment<MainPresenter>(), MainView {
                     logoView.setImageDrawable(a)
                 }
             })
-        qrCard!!.setDrawable(prepareAccountQr(context, account.uri))
-        accountSettingsRow!!.adapter.notifyItemRangeChanged(QR_ITEM_POSITION, 1)
+        qrCard?.setDrawable(prepareAccountQr(context, account.uri))
+        accountSettingsRow?.adapter!!.notifyItemRangeChanged(QR_ITEM_POSITION, 1)
     }
 
     override fun showExportDialog(pAccountID: String, hasPassword: Boolean) {
@@ -296,16 +291,12 @@ class MainFragment : BaseBrowseFragment<MainPresenter>(), MainView {
                     .commit()
             } else if (item is IconCard) {
                 when (item.type) {
-                    Card.Type.ACCOUNT_ADD_DEVICE -> presenter!!.onExportClicked()
-                    Card.Type.ACCOUNT_EDIT_PROFILE -> presenter!!.onEditProfileClicked()
+                    Card.Type.ACCOUNT_ADD_DEVICE -> presenter.onExportClicked()
+                    Card.Type.ACCOUNT_EDIT_PROFILE -> presenter.onEditProfileClicked()
                     Card.Type.ACCOUNT_SHARE_ACCOUNT -> {
                         val view = (itemViewHolder.view as CardView).mainImageView
                         val intent = Intent(activity, TVShareActivity::class.java)
-                        val bundle = ActivityOptionsCompat.makeSceneTransitionAnimation(
-                            requireActivity(),
-                            view,
-                            TVShareActivity.SHARED_ELEMENT_NAME
-                        ).toBundle()
+                        val bundle = ActivityOptionsCompat.makeSceneTransitionAnimation(requireActivity(), view, TVShareActivity.SHARED_ELEMENT_NAME).toBundle()
                         requireActivity().startActivity(intent, bundle)
                     }
                     Card.Type.ADD_CONTACT -> startActivity(Intent(activity, SearchActivity::class.java))
@@ -317,7 +308,7 @@ class MainFragment : BaseBrowseFragment<MainPresenter>(), MainView {
     }
 
     companion object {
-        private val TAG = MainFragment::class.java.simpleName
+        private val TAG = MainFragment::class.simpleName!!
 
         // Sections headers ids
         private const val HEADER_CONTACTS: Long = 0
@@ -348,7 +339,7 @@ class MainFragment : BaseBrowseFragment<MainPresenter>(), MainView {
                 val targetSize = (AvatarFactory.SIZE_NOTIF * context.resources.displayMetrics.density).toInt()
                 val targetPaddingSize = (AvatarFactory.SIZE_PADDING * context.resources.displayMetrics.density).toInt()
                 ChannelLogoUtils.storeChannelLogo(
-                    context, channelId, drawableToBitmap(context.getDrawable(R.drawable.ic_jami_48)!!, targetSize, targetPaddingSize))
+                    context, channelId, BitmapUtils.drawableToBitmap(context.getDrawable(R.drawable.ic_jami_48)!!, targetSize, targetPaddingSize))
                 TvContractCompat.requestChannelBrowsable(context, channelId)
             } else {
                 cr.update(TvContractCompat.buildChannelUri(channelId), channel.toContentValues(), null, null)
@@ -356,19 +347,14 @@ class MainFragment : BaseBrowseFragment<MainPresenter>(), MainView {
             return channelId
         }
 
-        private fun buildProgram(
-            context: Context,
-            vm: SmartListViewModel,
-            launcherName: String?,
-            channelId: Long
-        ): Single<PreviewProgram> {
+        private fun buildProgram(context: Context, vm: SmartListViewModel, launcherName: String?, channelId: Long): Single<PreviewProgram> {
             return AvatarDrawable.Builder()
                 .withViewModel(vm)
                 .withPresence(false)
                 .buildAsync(context)
-                .map { avatar: AvatarDrawable? ->
-                    val file = createImageFile(context)
-                    val bitmapAvatar = drawableToBitmap(avatar!!, 256)
+                .map { avatar: AvatarDrawable ->
+                    val file = AndroidFileUtils.createImageFile(context)
+                    val bitmapAvatar = BitmapUtils.drawableToBitmap(avatar, 256)
                     BufferedOutputStream(FileOutputStream(file)).use { os ->
                         bitmapAvatar.compress(Bitmap.CompressFormat.PNG, 100, os)
                     }
@@ -376,11 +362,8 @@ class MainFragment : BaseBrowseFragment<MainPresenter>(), MainView {
                     val uri = FileProvider.getUriForFile(context, ContentUriHandler.AUTHORITY_FILES, file)
 
                     // Grant permission to launcher
-                    if (launcherName != null) context.grantUriPermission(
-                        launcherName,
-                        uri,
-                        Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION
-                    )
+                    if (launcherName != null)
+                        context.grantUriPermission(launcherName, uri, Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION)
                     val contactBuilder = PreviewProgram.Builder()
                         .setChannelId(channelId)
                         .setType(TvContractCompat.PreviewPrograms.TYPE_CLIP)
@@ -388,15 +371,13 @@ class MainFragment : BaseBrowseFragment<MainPresenter>(), MainView {
                         .setAuthor(vm.contacts[0].ringUsername)
                         .setPosterArtAspectRatio(TvContractCompat.PreviewPrograms.ASPECT_RATIO_1_1)
                         .setPosterArtUri(uri)
-                        .setIntentUri(
-                            Uri.Builder()
-                                .scheme(ContentUriHandler.SCHEME_TV)
-                                .authority(ContentUriHandler.AUTHORITY)
-                                .appendPath(ContentUriHandler.PATH_TV_CONVERSATION)
-                                .appendPath(vm.accountId)
-                                .appendPath(vm.uri.uri)
-                                .build()
-                        )
+                        .setIntentUri(Uri.Builder()
+                            .scheme(ContentUriHandler.SCHEME_TV)
+                            .authority(ContentUriHandler.AUTHORITY)
+                            .appendPath(ContentUriHandler.PATH_TV_CONVERSATION)
+                            .appendPath(vm.accountId)
+                            .appendPath(vm.uri.uri)
+                            .build())
                         .setInternalProviderId(vm.uuid)
                     contactBuilder.build()
                 }
@@ -406,7 +387,7 @@ class MainFragment : BaseBrowseFragment<MainPresenter>(), MainView {
             Log.w(TAG, "prepareAccountQr $accountId")
             if (accountId == null || accountId.isEmpty()) return null
             val pad = 16
-            val qrCodeData = QRCodeUtils.encodeStringAsQRCodeData(accountId, 0X00000000, -0x1)
+            val qrCodeData = QRCodeUtils.encodeStringAsQRCodeData(accountId, 0X00000000, -0x1)!!
             val bitmap = Bitmap.createBitmap(qrCodeData.width + 2 * pad, qrCodeData.height + 2 * pad, Bitmap.Config.ARGB_8888)
             bitmap.setPixels(
                 qrCodeData.data,
diff --git a/ring-android/app/src/main/java/cx/ring/tv/main/MainPresenter.kt b/ring-android/app/src/main/java/cx/ring/tv/main/MainPresenter.kt
index 78fd9ff6d..131d377ef 100644
--- a/ring-android/app/src/main/java/cx/ring/tv/main/MainPresenter.kt
+++ b/ring-android/app/src/main/java/cx/ring/tv/main/MainPresenter.kt
@@ -46,7 +46,7 @@ class MainPresenter @Inject constructor(
     }
 
     private fun loadConversations() {
-        view!!.showLoading(true)
+        view?.showLoading(true)
         mCompositeDisposable.add(mConversationFacade.getSmartList(true)
             .switchMap { viewModels: List<Observable<SmartListViewModel>> ->
                 if (viewModels.isEmpty()) SmartListViewModel.EMPTY_RESULTS
@@ -56,10 +56,10 @@ class MainPresenter @Inject constructor(
             .throttleLatest(150, TimeUnit.MILLISECONDS, mUiScheduler)
             .observeOn(mUiScheduler)
             .subscribe({ viewModels: List<SmartListViewModel> ->
-                val view = view
-                view!!.showLoading(false)
+                val view = view ?: return@subscribe
+                view.showLoading(false)
                 view.showContacts(viewModels)
-            }) { e: Throwable? -> Log.w(TAG, "showConversations error ", e) })
+            }) { e: Throwable -> Log.w(TAG, "showConversations error ", e) })
         mCompositeDisposable.add(mConversationFacade.pendingList
             .switchMap { viewModels: List<Observable<SmartListViewModel>> ->
                 if (viewModels.isEmpty()) SmartListViewModel.EMPTY_RESULTS
@@ -73,10 +73,10 @@ class MainPresenter @Inject constructor(
             }) { e: Throwable -> Log.w(TAG, "showConversations error ", e) })
     }
 
-    fun reloadAccountInfo() {
-        mCompositeDisposable.add(mAccountService.currentAccountSubject
+    private fun reloadAccountInfo() {
+        mCompositeDisposable.add(mAccountService.currentProfileAccountSubject
             .observeOn(mUiScheduler)
-            .subscribe({ account: Account -> view?.displayAccountInfo(HomeNavigationViewModel(account, null)) })
+            .subscribe({ accountProfile -> view?.displayAccountInfo(HomeNavigationViewModel(accountProfile.first, accountProfile.second)) })
             { e: Throwable -> Log.d(TAG, "reloadAccountInfos getProfileAccountList onError", e) })
 
         mCompositeDisposable.add(mAccountService.observableAccounts
@@ -90,11 +90,11 @@ class MainPresenter @Inject constructor(
     }
 
     fun onEditProfileClicked() {
-        view!!.showProfileEditing()
+        view?.showProfileEditing()
     }
 
     fun onShareAccountClicked() {
-        view!!.showAccountShare()
+        view?.showAccountShare()
     }
 
     fun onSettingsClicked() {
diff --git a/ring-android/app/src/main/java/cx/ring/tv/main/MainView.java b/ring-android/app/src/main/java/cx/ring/tv/main/MainView.java
index be65e03e8..34223adab 100644
--- a/ring-android/app/src/main/java/cx/ring/tv/main/MainView.java
+++ b/ring-android/app/src/main/java/cx/ring/tv/main/MainView.java
@@ -38,8 +38,6 @@ public interface MainView {
 
     void callContact(String accountID, String ringID);
 
-    void displayErrorToast(Error error);
-
     void displayAccountInfo(HomeNavigationViewModel viewModel);
 
     void updateModel(Account account);
diff --git a/ring-android/app/src/main/java/cx/ring/tv/main/SpinnerFragment.java b/ring-android/app/src/main/java/cx/ring/tv/main/SpinnerFragment.java
deleted file mode 100644
index f89b3805f..000000000
--- a/ring-android/app/src/main/java/cx/ring/tv/main/SpinnerFragment.java
+++ /dev/null
@@ -1,53 +0,0 @@
-package cx.ring.tv.main;
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
- * in compliance with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software distributed under the License
- * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
- * or implied. See the License for the specific language governing permissions and limitations under
- * the License.
- */
-
-
-import android.os.Bundle;
-import androidx.fragment.app.Fragment;
-import android.view.Gravity;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.FrameLayout;
-import android.widget.ProgressBar;
-
-/**
- * SpinnerFragment shows spinning progressbar to notify user that
- * application is processing something (while downloading, or preparing sth etc.)
- * <p>
- * Example of usage in AsyncTask
- * + Start showing: OnPreExecute
- * mSpinnerFragment = new SpinnerFragment();
- * getFragmentManager().beginTransaction().add(R.id.some_view_group, mSpinnerFragment).commit();
- * + Stop showing: OnPostExecute
- * getFragmentManager().beginTransaction().remove(mSpinnerFragment).commit();
- */
-public class SpinnerFragment extends Fragment {
-
-    private static final int SPINNER_WIDTH = 100;
-    private static final int SPINNER_HEIGHT = 100;
-
-    @Override
-    public View onCreateView(LayoutInflater inflater, ViewGroup container,
-                             Bundle savedInstanceState) {
-        ProgressBar progressBar = new ProgressBar(container.getContext());
-        if (container instanceof FrameLayout) {
-            FrameLayout.LayoutParams layoutParams =
-                    new FrameLayout.LayoutParams(SPINNER_WIDTH, SPINNER_HEIGHT, Gravity.CENTER);
-            progressBar.setLayoutParams(layoutParams);
-        }
-        return progressBar;
-    }
-}
diff --git a/ring-android/app/src/main/java/cx/ring/tv/main/SpinnerFragment.kt b/ring-android/app/src/main/java/cx/ring/tv/main/SpinnerFragment.kt
new file mode 100644
index 000000000..9a017c4a2
--- /dev/null
+++ b/ring-android/app/src/main/java/cx/ring/tv/main/SpinnerFragment.kt
@@ -0,0 +1,26 @@
+package cx.ring.tv.main
+
+import android.view.LayoutInflater
+import android.view.ViewGroup
+import android.os.Bundle
+import android.widget.FrameLayout
+import android.view.Gravity
+import android.view.View
+import android.widget.ProgressBar
+import androidx.fragment.app.Fragment
+
+class SpinnerFragment : Fragment() {
+    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
+        val progressBar = ProgressBar(container!!.context)
+        if (container is FrameLayout) {
+            val layoutParams = FrameLayout.LayoutParams(SPINNER_WIDTH, SPINNER_HEIGHT, Gravity.CENTER)
+            progressBar.layoutParams = layoutParams
+        }
+        return progressBar
+    }
+
+    companion object {
+        private const val SPINNER_WIDTH = 100
+        private const val SPINNER_HEIGHT = 100
+    }
+}
\ No newline at end of file
diff --git a/ring-android/app/src/main/java/cx/ring/tv/search/BaseSearchFragment.java b/ring-android/app/src/main/java/cx/ring/tv/search/BaseSearchFragment.java
index caa20fa1d..0b43e227a 100644
--- a/ring-android/app/src/main/java/cx/ring/tv/search/BaseSearchFragment.java
+++ b/ring-android/app/src/main/java/cx/ring/tv/search/BaseSearchFragment.java
@@ -21,27 +21,20 @@ package cx.ring.tv.search;
 
 import android.os.Bundle;
 import android.view.View;
-import android.widget.Toast;
 
+import androidx.annotation.NonNull;
 import androidx.leanback.app.SearchSupportFragment;
 
 import javax.inject.Inject;
 
-import cx.ring.R;
-import net.jami.model.Error;
-import net.jami.mvp.BaseView;
 import net.jami.mvp.RootPresenter;
 
-public class BaseSearchFragment<T extends RootPresenter> extends SearchSupportFragment
-        implements BaseView {
-
-    protected static final String TAG = BaseSearchFragment.class.getSimpleName();
-
+public class BaseSearchFragment<T extends RootPresenter> extends SearchSupportFragment {
     @Inject
     protected T presenter;
 
     @Override
-    public void onViewCreated(View view, Bundle savedInstanceState) {
+    public void onViewCreated(@NonNull View view, Bundle savedInstanceState) {
         super.onViewCreated(view, savedInstanceState);
 
         //Be sure to do the injection in onCreateView method
@@ -55,24 +48,6 @@ public class BaseSearchFragment<T extends RootPresenter> extends SearchSupportFr
         presenter.unbindView();
     }
 
-    @Override
-    public void displayErrorToast(Error error) {
-        String errorString;
-        switch (error) {
-            case NO_MICROPHONE:
-                errorString = getString(R.string.call_error_no_microphone);
-                break;
-            case NO_INPUT:
-                errorString = getString(R.string.call_error_no_camera_no_microphone);
-                break;
-            default:
-                errorString = getString(R.string.generic_error);
-                break;
-        }
-
-        Toast.makeText(getActivity(), errorString, Toast.LENGTH_LONG).show();
-    }
-
     protected void initPresenter(T presenter) {
 
     }
diff --git a/ring-android/app/src/main/java/cx/ring/tv/search/ContactSearchFragment.kt b/ring-android/app/src/main/java/cx/ring/tv/search/ContactSearchFragment.kt
index fe5843efc..0dce916bd 100644
--- a/ring-android/app/src/main/java/cx/ring/tv/search/ContactSearchFragment.kt
+++ b/ring-android/app/src/main/java/cx/ring/tv/search/ContactSearchFragment.kt
@@ -52,7 +52,7 @@ class ContactSearchFragment : BaseSearchFragment<ContactSearchPresenter>(),
         super.onCreate(savedInstanceState)
         setSearchResultProvider(this)
         setOnItemViewClickedListener { _, item: Any, _, _ ->
-            presenter.contactClicked((item as ContactCard).model)
+            presenter.contactClicked((item as ContactCard).model!!)
         }
         badgeDrawable = ContextCompat.getDrawable(requireContext(), R.mipmap.ic_launcher)
         setSearchQuery("", false)
diff --git a/ring-android/app/src/main/java/cx/ring/tv/search/ContactSearchPresenter.java b/ring-android/app/src/main/java/cx/ring/tv/search/ContactSearchPresenter.java
deleted file mode 100644
index 3e875738d..000000000
--- a/ring-android/app/src/main/java/cx/ring/tv/search/ContactSearchPresenter.java
+++ /dev/null
@@ -1,125 +0,0 @@
-/*
- *  Copyright (C) 2004-2021 Savoir-faire Linux Inc.
- *
- *  Author: Alexandre Lision <alexandre.lision@savoirfairelinux.com>
- *  Author: Adrien Béraud <adrien.beraud@savoirfairelinux.com>
- *
- *  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, write to the Free Software
- *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-package cx.ring.tv.search;
-
-import java.util.concurrent.TimeUnit;
-
-import javax.inject.Inject;
-import javax.inject.Named;
-
-import net.jami.model.Account;
-import net.jami.model.Contact;
-import net.jami.model.Uri;
-import net.jami.mvp.RootPresenter;
-import net.jami.services.AccountService;
-import net.jami.services.HardwareService;
-import net.jami.services.VCardService;
-import net.jami.smartlist.SmartListViewModel;
-
-import io.reactivex.rxjava3.core.Scheduler;
-import io.reactivex.rxjava3.subjects.PublishSubject;
-
-public class ContactSearchPresenter extends RootPresenter<ContactSearchView> {
-
-    private final AccountService mAccountService;
-
-    private Contact mContact;
-    @Inject
-    @Named("UiScheduler")
-    protected Scheduler mUiScheduler;
-
-    private final PublishSubject<String> contactQuery = PublishSubject.create();
-
-    @Inject
-    public ContactSearchPresenter(AccountService accountService) {
-        mAccountService = accountService;
-    }
-
-    @Override
-    public void bindView(ContactSearchView view) {
-        super.bindView(view);
-        mCompositeDisposable.add(contactQuery
-                .debounce(350, TimeUnit.MILLISECONDS)
-                .switchMapSingle(q -> mAccountService.findRegistrationByName(mAccountService.getCurrentAccount().getAccountID(), "", q))
-                .observeOn(mUiScheduler)
-                .subscribe(q -> parseEventState(mAccountService.getAccount(q.getAccountId()), q.getName(), q.getAddress(), q.getState())));
-    }
-
-    @Override
-    public void unbindView() {
-        super.unbindView();
-    }
-
-    public void queryTextChanged(String query) {
-        if (query.equals("")) {
-            getView().clearSearch();
-        } else {
-            Account currentAccount = mAccountService.getCurrentAccount();
-            if (currentAccount == null) {
-                return;
-            }
-
-            Uri uri = Uri.fromString(query);
-            if (uri.isHexId()) {
-                mContact = currentAccount.getContactFromCache(uri);
-                getView().displayContact(currentAccount.getAccountID(), mContact);
-            } else {
-                getView().clearSearch();
-                contactQuery.onNext(query);
-            }
-        }
-    }
-
-    private void parseEventState(Account account, String name, String address, int state) {
-        switch (state) {
-            case 0:
-                // on found
-                mContact = account.getContactFromCache(address);
-                mContact.setUsername(name);
-                getView().displayContact(account.getAccountID(), mContact);
-                break;
-            case 1:
-                // invalid name
-                Uri uriName = Uri.fromString(name);
-                if (uriName.isHexId()) {
-                    mContact = account.getContactFromCache(uriName);
-                    getView().displayContact(account.getAccountID(), mContact);
-                } else {
-                    getView().clearSearch();
-                }
-                break;
-            default:
-                // on error
-                Uri uriAddress = Uri.fromString(address);
-                if (uriAddress.isHexId()) {
-                    mContact = account.getContactFromCache(uriAddress);
-                    getView().displayContact(account.getAccountID(), mContact);
-                } else {
-                    getView().clearSearch();
-                }
-                break;
-        }
-    }
-
-    public void contactClicked(SmartListViewModel model) {
-        getView().displayContactDetails(model);
-    }
-}
\ No newline at end of file
diff --git a/ring-android/app/src/main/java/cx/ring/tv/search/ContactSearchPresenter.kt b/ring-android/app/src/main/java/cx/ring/tv/search/ContactSearchPresenter.kt
new file mode 100644
index 000000000..e9b05edd1
--- /dev/null
+++ b/ring-android/app/src/main/java/cx/ring/tv/search/ContactSearchPresenter.kt
@@ -0,0 +1,103 @@
+/*
+ *  Copyright (C) 2004-2021 Savoir-faire Linux Inc.
+ *
+ *  Author: Alexandre Lision <alexandre.lision@savoirfairelinux.com>
+ *  Author: Adrien Béraud <adrien.beraud@savoirfairelinux.com>
+ *
+ *  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, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+package cx.ring.tv.search
+
+import io.reactivex.rxjava3.core.Scheduler
+import io.reactivex.rxjava3.subjects.PublishSubject
+import net.jami.model.Account
+import net.jami.model.Contact
+import net.jami.model.Uri.Companion.fromString
+import net.jami.mvp.RootPresenter
+import net.jami.services.AccountService
+import net.jami.services.AccountService.RegisteredName
+import net.jami.smartlist.SmartListViewModel
+import java.util.concurrent.TimeUnit
+import javax.inject.Inject
+import javax.inject.Named
+
+class ContactSearchPresenter @Inject constructor(
+    private val mAccountService: AccountService,
+    @Named("UiScheduler") var mUiScheduler: Scheduler
+) : RootPresenter<ContactSearchView>() {
+    private var mContact: Contact? = null
+    private val contactQuery = PublishSubject.create<String>()
+
+    override fun bindView(view: ContactSearchView) {
+        super.bindView(view)
+        mCompositeDisposable.add(contactQuery
+            .debounce(350, TimeUnit.MILLISECONDS)
+            .switchMapSingle { q: String ->
+                mAccountService.findRegistrationByName(mAccountService.currentAccount!!.accountID, "", q)
+            }
+            .observeOn(mUiScheduler)
+            .subscribe { q: RegisteredName -> parseEventState(mAccountService.getAccount(q.accountId)!!, q.name, q.address, q.state) })
+    }
+
+    fun queryTextChanged(query: String) {
+        if (query == "") {
+            view?.clearSearch()
+        } else {
+            val currentAccount = mAccountService.currentAccount ?: return
+            val uri = fromString(query)
+            if (uri.isHexId) {
+                mContact = currentAccount.getContactFromCache(uri)
+                view!!.displayContact(currentAccount.accountID, mContact)
+            } else {
+                view!!.clearSearch()
+                contactQuery.onNext(query)
+            }
+        }
+    }
+
+    private fun parseEventState(account: Account, name: String, address: String?, state: Int) {
+        when (state) {
+            0 -> {
+                // on found
+                mContact = account.getContactFromCache(address!!).apply { setUsername(name) }
+                view?.displayContact(account.accountID, mContact)
+            }
+            1 -> {
+                // invalid name
+                val uriName = fromString(name)
+                if (uriName.isHexId) {
+                    mContact = account.getContactFromCache(uriName)
+                    view?.displayContact(account.accountID, mContact)
+                } else {
+                    view?.clearSearch()
+                }
+            }
+            else -> {
+                // on error
+                val uriAddress = fromString(address!!)
+                if (uriAddress.isHexId) {
+                    mContact = account.getContactFromCache(uriAddress)
+                    view?.displayContact(account.accountID, mContact)
+                } else {
+                    view?.clearSearch()
+                }
+            }
+        }
+    }
+
+    fun contactClicked(model: SmartListViewModel) {
+        view?.displayContactDetails(model)
+    }
+}
\ No newline at end of file
diff --git a/ring-android/app/src/main/java/cx/ring/tv/search/ContactSearchView.java b/ring-android/app/src/main/java/cx/ring/tv/search/ContactSearchView.java
index e011340a1..14bc3b85c 100644
--- a/ring-android/app/src/main/java/cx/ring/tv/search/ContactSearchView.java
+++ b/ring-android/app/src/main/java/cx/ring/tv/search/ContactSearchView.java
@@ -29,8 +29,6 @@ public interface ContactSearchView {
 
     void clearSearch();
 
-    void displayErrorToast(Error error);
-
     void startCall(String accountID, String number);
 
     void displayContactDetails(SmartListViewModel model);
diff --git a/ring-android/app/src/main/java/cx/ring/tv/settings/TVSettingsFragment.java b/ring-android/app/src/main/java/cx/ring/tv/settings/TVSettingsFragment.java
index 365daf3f5..90dbb1ab5 100644
--- a/ring-android/app/src/main/java/cx/ring/tv/settings/TVSettingsFragment.java
+++ b/ring-android/app/src/main/java/cx/ring/tv/settings/TVSettingsFragment.java
@@ -39,9 +39,8 @@ import java.util.ArrayList;
 import java.util.Arrays;
 
 import cx.ring.R;
-import cx.ring.application.JamiApplication;
-import cx.ring.fragments.GeneralAccountPresenter;
-import cx.ring.fragments.GeneralAccountView;
+import net.jami.settings.GeneralAccountPresenter;
+import net.jami.settings.GeneralAccountView;
 import net.jami.model.Account;
 import net.jami.model.ConfigKey;
 import net.jami.utils.Tuple;
@@ -100,7 +99,7 @@ public class TVSettingsFragment extends LeanbackSettingsFragmentCompat {
         }
 
         @Override
-        public void addJamiPreferences(String accountId) {
+        public void addJamiPreferences(@NonNull String accountId) {
 
         }
 
diff --git a/ring-android/app/src/main/java/cx/ring/utils/AndroidFileUtils.kt b/ring-android/app/src/main/java/cx/ring/utils/AndroidFileUtils.kt
index ff6f2339b..f91e41056 100644
--- a/ring-android/app/src/main/java/cx/ring/utils/AndroidFileUtils.kt
+++ b/ring-android/app/src/main/java/cx/ring/utils/AndroidFileUtils.kt
@@ -220,7 +220,7 @@ object AndroidFileUtils {
             getMimeType(uri.toString())
     }
 
-    fun getMimeType(filename: String): String? {
+    fun getMimeType(filename: String): String {
         val pos = filename.lastIndexOf(".")
         var fileExtension: String? = null
         if (pos >= 0) {
@@ -522,8 +522,8 @@ object AndroidFileUtils {
     private fun getOrientation(context: Context, photoUri: Uri): Int {
         val resolver = context.contentResolver ?: return 0
         try {
-            resolver.query(photoUri, arrayOf(MediaStore.Images.ImageColumns.ORIENTATION), null, null, null).use { cursor ->
-                cursor!!.moveToFirst()
+            resolver.query(photoUri, arrayOf(MediaStore.Images.ImageColumns.ORIENTATION), null, null, null)!!.use { cursor ->
+                cursor.moveToFirst()
                 return cursor.getInt(0)
             }
         } catch (e: Exception) {
@@ -539,8 +539,8 @@ object AndroidFileUtils {
     private fun getExifOrientation(resolver: ContentResolver, photoUri: Uri): Int {
         if (Build.VERSION.SDK_INT > 23) {
             try {
-                resolver.openInputStream(photoUri).use { input ->
-                    return ExifInterface(input!!)
+                resolver.openInputStream(photoUri)!!.use { input ->
+                    return ExifInterface(input)
                             .getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL)
                 }
             } catch (e: Exception) {
@@ -556,14 +556,11 @@ object AndroidFileUtils {
         }
     }
 
-    @JvmStatic
     fun isImage(s: String): Boolean {
-        return getMimeType(s)?.startsWith("image") ?: false
+        return getMimeType(s).startsWith("image")
     }
 
-    @JvmStatic
     fun getFileName(s: String): String {
-        val parts = s.split(Regex("/")).toTypedArray()
-        return parts[parts.size - 1]
+        return s.split('/').last()
     }
 }
\ No newline at end of file
diff --git a/ring-android/app/src/main/java/cx/ring/utils/ContentUriHandler.kt b/ring-android/app/src/main/java/cx/ring/utils/ContentUriHandler.kt
index 3e6d99dbd..08cc927a9 100644
--- a/ring-android/app/src/main/java/cx/ring/utils/ContentUriHandler.kt
+++ b/ring-android/app/src/main/java/cx/ring/utils/ContentUriHandler.kt
@@ -67,7 +67,6 @@ object ContentUriHandler {
         return getUriForFile(context, authority, file, null)
     }
 
-    @JvmStatic
     fun getUriForFile(context: Context, authority: String, file: File, displayName: String?): Uri {
         return try {
             if (displayName == null)
diff --git a/ring-android/app/src/main/java/cx/ring/utils/ConversationPath.kt b/ring-android/app/src/main/java/cx/ring/utils/ConversationPath.kt
index fbd26a1dc..24619fcbd 100644
--- a/ring-android/app/src/main/java/cx/ring/utils/ConversationPath.kt
+++ b/ring-android/app/src/main/java/cx/ring/utils/ConversationPath.kt
@@ -27,7 +27,6 @@ import cx.ring.BuildConfig
 import net.jami.model.Conversation
 import net.jami.model.Uri
 import net.jami.utils.StringUtils
-import net.jami.utils.Tuple
 import java.util.*
 
 class ConversationPath {
@@ -46,11 +45,6 @@ class ConversationPath {
         conversationId = conversationUri.uri
     }
 
-    constructor(path: Tuple<String, String>) {
-        accountId = path.first
-        conversationId = path.second
-    }
-
     constructor(conversation: Conversation) {
         accountId = conversation.accountId
         conversationId = conversation.uri.uri
@@ -117,7 +111,7 @@ class ConversationPath {
             return if (interaction.conversation is Conversation)
                 toUri(interaction.account, (interaction.conversation as Conversation).uri)
             else
-                toUri(interaction.account, Uri.fromString(interaction.conversation!!.participant))
+                toUri(interaction.account, Uri.fromString(interaction.conversation!!.participant!!))
         }
 
         fun toBundle(accountId: String, uri: String): Bundle {
diff --git a/ring-android/app/src/main/java/cx/ring/utils/NetworkUtils.java b/ring-android/app/src/main/java/cx/ring/utils/NetworkUtils.java
deleted file mode 100644
index 574e104e2..000000000
--- a/ring-android/app/src/main/java/cx/ring/utils/NetworkUtils.java
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- *  Copyright (C) 2004-2021 Savoir-faire Linux Inc.
- *
- *  Author: Hadrien Desousa <hadrien.desousa@savoirfairelinux.com>
- *
- *  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, write to the Free Software
- *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-package cx.ring.utils;
-
-import android.content.Context;
-import android.net.ConnectivityManager;
-import android.net.Network;
-import android.net.NetworkCapabilities;
-import android.net.NetworkInfo;
-
-public final class NetworkUtils {
-    /**
-     * Get the network info
-     */
-    public static NetworkInfo getNetworkInfo(Context context) {
-        if (context == null)
-            return null;
-        ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
-        if (cm == null)
-            return null;
-        for (Network n: cm.getAllNetworks()) {
-            NetworkCapabilities caps = cm.getNetworkCapabilities(n);
-            if (caps != null && !caps.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET))
-                continue;
-            NetworkInfo nInfo = cm.getNetworkInfo(n);
-            if(nInfo != null && nInfo.isConnected())
-                return nInfo;
-        }
-        return null;
-    }
-
-    public static boolean isConnectivityAllowed(Context context) {
-        NetworkInfo info = NetworkUtils.getNetworkInfo(context);
-        return info != null && info.isConnected();
-    }
-    public static boolean isPushAllowed(Context context, boolean allowMobile) {
-        if (allowMobile)
-            return true;
-        NetworkInfo info = NetworkUtils.getNetworkInfo(context);
-        return info != null && info.getType() != ConnectivityManager.TYPE_MOBILE;
-    }
-}
diff --git a/ring-android/app/src/main/java/cx/ring/utils/NetworkUtils.kt b/ring-android/app/src/main/java/cx/ring/utils/NetworkUtils.kt
new file mode 100644
index 000000000..ac49d93d2
--- /dev/null
+++ b/ring-android/app/src/main/java/cx/ring/utils/NetworkUtils.kt
@@ -0,0 +1,53 @@
+/*
+ *  Copyright (C) 2004-2021 Savoir-faire Linux Inc.
+ *
+ *  Author: Hadrien Desousa <hadrien.desousa@savoirfairelinux.com>
+ *
+ *  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, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+package cx.ring.utils
+
+import android.content.Context
+import android.net.NetworkInfo
+import android.net.ConnectivityManager
+import android.net.NetworkCapabilities
+
+object NetworkUtils {
+    /**
+     * Get the network info
+     */
+    fun getNetworkInfo(context: Context): NetworkInfo? {
+        val cm = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
+        for (n in cm.allNetworks) {
+            val caps = cm.getNetworkCapabilities(n)
+            if (caps != null && !caps.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)) continue
+            val nInfo = cm.getNetworkInfo(n)
+            if (nInfo != null && nInfo.isConnected)
+                return nInfo
+        }
+        return null
+    }
+
+    fun isConnectivityAllowed(context: Context): Boolean {
+        val info = getNetworkInfo(context)
+        return info != null && info.isConnected
+    }
+
+    fun isPushAllowed(context: Context, allowMobile: Boolean): Boolean {
+        if (allowMobile) return true
+        val info = getNetworkInfo(context)
+        return info != null && info.type != ConnectivityManager.TYPE_MOBILE
+    }
+}
\ No newline at end of file
diff --git a/ring-android/app/src/main/java/cx/ring/viewholders/SmartListViewHolder.kt b/ring-android/app/src/main/java/cx/ring/viewholders/SmartListViewHolder.kt
index e99b90922..1f78f9e10 100644
--- a/ring-android/app/src/main/java/cx/ring/viewholders/SmartListViewHolder.kt
+++ b/ring-android/app/src/main/java/cx/ring/viewholders/SmartListViewHolder.kt
@@ -143,7 +143,7 @@ class SmartListViewHolder : RecyclerView.ViewHolder {
     }
 
     interface SmartListListeners {
-        fun onItemClick(smartListViewModel: SmartListViewModel)
-        fun onItemLongClick(smartListViewModel: SmartListViewModel)
+        fun onItemClick(item: SmartListViewModel)
+        fun onItemLongClick(item: SmartListViewModel)
     }
 }
\ No newline at end of file
diff --git a/ring-android/app/src/main/java/cx/ring/views/AvatarDrawable.kt b/ring-android/app/src/main/java/cx/ring/views/AvatarDrawable.kt
index 2e143fc69..5984ba926 100644
--- a/ring-android/app/src/main/java/cx/ring/views/AvatarDrawable.kt
+++ b/ring-android/app/src/main/java/cx/ring/views/AvatarDrawable.kt
@@ -34,9 +34,9 @@ import io.reactivex.rxjava3.core.Single
 import net.jami.model.Account
 import net.jami.model.Contact
 import net.jami.model.Conversation
+import net.jami.model.Profile
 import net.jami.smartlist.SmartListViewModel
 import net.jami.utils.HashUtils
-import net.jami.utils.Tuple
 import java.util.*
 
 class AvatarDrawable : Drawable {
@@ -92,10 +92,10 @@ class AvatarDrawable : Drawable {
             return VCardServiceImpl.loadProfile(context, account)
                 .map { profile -> build(context, account, profile, crop) }
         }
-        fun build(context: Context, account: Account, profile: Tuple<String?, Any?>, crop: Boolean): AvatarDrawable {
+        fun build(context: Context, account: Account, profile: Profile, crop: Boolean = true): AvatarDrawable {
             return Builder()
-                        .withPhoto(profile.second as Bitmap?)
-                        .withNameData(profile.first, account.registeredName)
+                        .withPhoto(profile.avatar as Bitmap?)
+                        .withNameData(profile.displayName, account.registeredName)
                         .withId(account.uri)
                         .withCircleCrop(crop)
                         .build(context)
@@ -294,7 +294,9 @@ class AvatarDrawable : Drawable {
         avatarText = convertNameToAvatarText(
             if (TextUtils.isEmpty(profileName)) username else profileName
         )
-        bitmaps?.set(0, contact.photo as Bitmap)
+        contact.photo?.let { photo ->
+            bitmaps?.set(0, photo as Bitmap)
+        }
         isOnline = contact.isOnline
         update = true
     }
@@ -618,14 +620,7 @@ class AvatarDrawable : Drawable {
                     val bitmap = bitmaps[i]
                     val subBounds = getSubBounds(realBounds, bitmaps.size, i)
                     if (subBounds != null) {
-                        fit(
-                            bitmap.width,
-                            bitmap.height,
-                            subBounds.width(),
-                            subBounds.height(),
-                            false,
-                            inBounds!![i]
-                        )
+                        fit(bitmap.width, bitmap.height, subBounds.width(), subBounds.height(), false, inBounds!![i])
                         backgroundBounds!![i].set(subBounds)
                     }
                 }
diff --git a/ring-android/app/src/main/java/cx/ring/views/SwitchButton.java b/ring-android/app/src/main/java/cx/ring/views/SwitchButton.java
deleted file mode 100644
index a5f238032..000000000
--- a/ring-android/app/src/main/java/cx/ring/views/SwitchButton.java
+++ /dev/null
@@ -1,529 +0,0 @@
-/*
- *  Copyright (C) 2004-2021 Savoir-faire Linux Inc.
- *
- *  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, write to the Free Software
- *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-package cx.ring.views;
-
-import android.animation.ObjectAnimator;
-import android.animation.ValueAnimator;
-import android.content.Context;
-import android.content.res.TypedArray;
-import android.graphics.Canvas;
-import android.graphics.Color;
-import android.graphics.Paint;
-import android.graphics.PointF;
-import android.graphics.RectF;
-import android.graphics.drawable.LayerDrawable;
-import android.graphics.drawable.RotateDrawable;
-import android.os.Build;
-import android.text.Layout;
-import android.text.StaticLayout;
-import android.text.TextUtils;
-import android.util.AttributeSet;
-import android.util.TypedValue;
-import android.view.MotionEvent;
-import android.view.SoundEffectConstants;
-import android.view.ViewConfiguration;
-import android.view.ViewParent;
-import android.view.animation.AccelerateDecelerateInterpolator;
-import android.widget.CompoundButton;
-
-import androidx.annotation.Nullable;
-import androidx.core.content.res.ResourcesCompat;
-
-import cx.ring.R;
-
-public class SwitchButton extends CompoundButton {
-    public static final int DEFAULT_THUMB_SIZE_DP = 20;
-    public static final int DEFAULT_THUMB_MARGIN_DP = 2;
-    public static final int DEFAULT_ANIMATION_DURATION = 250;
-    public static final int DEFAULT_SWITCH_WIDTH = 72;
-
-    private int mBackColor;
-    private final int mThumbSize;
-    private int mBackWidth;
-    private int mBackHeight;
-    private final int mTouchSlop;
-    private final int mClickTimeout;
-    private float mThumbRadius, mBackRadius;
-    private float mTextWidth;
-    private float mTextHeight;
-    private float mProgress;
-    private float mStartX, mStartY, mLastX;
-    private boolean mReady = false;
-    private boolean mCatch = false;
-    private boolean mShowImage = false;
-
-    private final PointF mThumbPos = new PointF(), mPresentThumbPos = new PointF();
-    private final RectF mBackRectF = new RectF(),
-            mSafeRectF = new RectF(),
-            mTextOnRectF = new RectF(),
-            mTextOffRectF = new RectF(),
-            mThumbMargin = new RectF();
-    private final Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
-    private final ValueAnimator mProgressAnimator = ValueAnimator.ofFloat(0, 0);
-    private CharSequence mStatus;
-    private Layout mOnLayout;
-    private Layout mOffLayout;
-    private final RotateDrawable mImageDrawable;
-
-    private boolean mChangingState = false;
-    private CompoundButton.OnCheckedChangeListener mChildOnCheckedChangeListener = null;
-
-    public SwitchButton(Context context) {
-        this(context, null);
-    }
-
-    public SwitchButton(Context context, AttributeSet attrs) {
-        this(context, attrs, 0);
-    }
-
-    public SwitchButton(Context context, @Nullable AttributeSet attrs, int defStyle) {
-        super(context, attrs, defStyle);
-        TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.SwitchButton);
-        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
-            saveAttributeDataForStyleable(context, R.styleable.SwitchButton, attrs, ta, defStyle, 0);
-        }
-
-        int backColor = ta.getColor(R.styleable.SwitchButton_backColor, getResources().getColor(R.color.grey_400));
-        String status = ta.getString(R.styleable.SwitchButton_status);
-        ta.recycle();
-
-        mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
-        mClickTimeout = ViewConfiguration.getPressedStateDuration() + ViewConfiguration.getTapTimeout();
-
-        mProgressAnimator.setInterpolator(new AccelerateDecelerateInterpolator());
-        mProgressAnimator.addUpdateListener(valueAnimator -> setProgress((float) valueAnimator.getAnimatedValue()));
-        mProgressAnimator.setDuration(DEFAULT_ANIMATION_DURATION);
-
-        LayerDrawable layerDrawable = (LayerDrawable) ResourcesCompat.getDrawable(getResources(), R.drawable.rotate, context.getTheme());
-        mImageDrawable = (RotateDrawable) layerDrawable.findDrawableByLayerId(R.id.progress);
-
-        setFocusable(true);
-        setClickable(true);
-
-        mStatus = status;
-        mBackColor = backColor;
-
-        float margin = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, DEFAULT_THUMB_MARGIN_DP, getResources().getDisplayMetrics());
-        mThumbMargin.set(margin, margin, margin, margin);
-        mThumbSize =  (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, DEFAULT_THUMB_SIZE_DP, getResources().getDisplayMetrics());
-
-        // size & measure params must larger than 1
-        //float thumbRangeRatio = DEFAULT_THUMB_RANGE_RATIO;
-        //mThumbRangeRatio = mThumbMargin.width() >= 0 ? Math.max(thumbRangeRatio, 1) : thumbRangeRatio;
-
-        // sync checked status
-        setProgress(isChecked() ? 1.f : 0.f);
-
-        super.setOnCheckedChangeListener((buttonView, isChecked) -> {
-            if (mChangingState)
-                return;
-            if (mChildOnCheckedChangeListener != null)
-                mChildOnCheckedChangeListener.onCheckedChanged(buttonView, isChecked);
-        });
-    }
-
-    private Layout makeLayout(CharSequence text) {
-        return new StaticLayout(text, getPaint(), (int) Math.ceil(Layout.getDesiredWidth(text, getPaint())), Layout.Alignment.ALIGN_CENTER, 1.f, 0, false);
-    }
-
-    @Override
-    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
-        if (mOnLayout == null && !TextUtils.isEmpty(mStatus)) {
-            mOnLayout = makeLayout(mStatus);
-        }
-        if (mOffLayout == null && !TextUtils.isEmpty(mStatus)) {
-            mOffLayout = makeLayout(mStatus);
-        }
-
-        float defaultWidth = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, DEFAULT_SWITCH_WIDTH, getResources().getDisplayMetrics());
-        //float onWidth = DEFAULT_SWITCH_WIDTH;
-//                mOnLayout != null ? mOnLayout.getWidth() : 0;
-        //float offWidth = DEFAULT_SWITCH_WIDTH;
-//                mOffLayout != null ? mOffLayout.getWidth() : 0;
-        /*if (onWidth != 0 || offWidth != 0) {
-            mTextWidth = Math.max(onWidth, offWidth);
-        } else {
-            mTextWidth = 0;
-        }*/
-        mTextWidth = defaultWidth;
-
-        float onHeight = mOnLayout != null ? mOnLayout.getHeight() : 0;
-        float offHeight = mOffLayout != null ? mOffLayout.getHeight() : 0;
-        if (onHeight != 0 || offHeight != 0) {
-            mTextHeight = Math.max(onHeight, offHeight);
-        } else {
-            mTextHeight = 0;
-        }
-
-        setMeasuredDimension(measureWidth(widthMeasureSpec), measureHeight(heightMeasureSpec));
-    }
-
-    /**
-     * SwitchButton use this formula to determine the final size of thumb, background and itself.
-     * <p>
-     * textWidth = max(onWidth, offWidth)
-     * thumbRange = thumbWidth * rangeRatio
-     * textExtraSpace = textWidth + textExtra - (moveRange - thumbWidth + max(thumbMargin.left, thumbMargin.right) + textThumbInset)
-     * backWidth = thumbRange + thumbMargin.left + thumbMargin.right + max(textExtraSpace, 0)
-     * contentSize = thumbRange + max(thumbMargin.left, 0) + max(thumbMargin.right, 0) + max(textExtraSpace, 0)
-     *
-     * @param widthMeasureSpec widthMeasureSpec
-     * @return measuredWidth
-     */
-    private int measureWidth(int widthMeasureSpec) {
-        int widthSize = MeasureSpec.getSize(widthMeasureSpec);
-        int widthMode = MeasureSpec.getMode(widthMeasureSpec);
-        int measuredWidth;
-
-        if (widthMode == MeasureSpec.EXACTLY) {
-            measuredWidth = widthSize;
-            mBackWidth = widthSize - getPaddingLeft() - getPaddingRight();
-        } else {
-            /*
-            If parent view want SwitchButton to determine it's size itself, we calculate the minimal
-            size of it's content. Further more, we ignore the limitation of widthSize since we want
-            to display SwitchButton in its actual size rather than compress the shape.
-             */
-            mBackWidth = Math.max(ceil(mThumbSize + mThumbMargin.left + mThumbMargin.right + mTextWidth), 0);
-            measuredWidth = mBackWidth + getPaddingLeft() + getPaddingRight();
-        }
-        return measuredWidth;
-    }
-
-    private int measureHeight(int heightMeasureSpec) {
-        int heightSize = MeasureSpec.getSize(heightMeasureSpec);
-        int heightMode = MeasureSpec.getMode(heightMeasureSpec);
-        int measuredHeight = heightSize;
-
-        int contentSize;
-        int textExtraSpace;
-        if (heightMode == MeasureSpec.EXACTLY) {
-            //if (mThumbSize != 0) {
-                /*
-                If thumbHeight has been set, we calculate backHeight and check if there is enough room.
-                 */
-                mBackHeight = ceil(mThumbSize + mThumbMargin.top + mThumbMargin.bottom);
-                mBackHeight = ceil(Math.max(mBackHeight, mTextHeight));
-                /*if (mBackHeight + getPaddingTop() + getPaddingBottom() - Math.min(0, mThumbMargin.top) - Math.min(0, mThumbMargin.bottom) > heightSize) {
-                    // No enough room, we set thumbHeight to zero to calculate these value again.
-                    mThumbHeight = 0;
-                }*/
-            //}
-
-            /*if (mThumbHeight == 0) {
-                mBackHeight = ceil(heightSize - getPaddingTop() - getPaddingBottom() + Math.min(0, mThumbMargin.top) + Math.min(0, mThumbMargin.bottom));
-                if (mBackHeight < 0) {
-                    mBackHeight = 0;
-                    mThumbHeight = 0;
-                    return measuredHeight;
-                }
-                mThumbHeight = ceil(mBackHeight - mThumbMargin.top - mThumbMargin.bottom);
-            }
-            if (mThumbHeight < 0) {
-                mBackHeight = 0;
-                mThumbHeight = 0;
-                return measuredHeight;
-            }*/
-        } else {
-            /*if (mThumbHeight == 0) {
-                mThumbHeight = ceil(getResources().getDisplayMetrics().density * DEFAULT_THUMB_SIZE_DP);
-            }*/
-            mBackHeight = ceil(mThumbSize + mThumbMargin.top + mThumbMargin.bottom);
-            /*if (mBackHeight < 0) {
-                mBackHeight = 0;
-                //mThumbHeight = 0;
-                return measuredHeight;
-            }*/
-            textExtraSpace = ceil(mTextHeight - mBackHeight);
-            if (textExtraSpace > 0) {
-                mBackHeight += textExtraSpace;
-                //mThumbHeight += textExtraSpace;
-            }
-            contentSize = Math.max(mThumbSize, mBackHeight);
-
-            measuredHeight = Math.max(contentSize, contentSize + getPaddingTop() + getPaddingBottom());
-            measuredHeight = Math.max(measuredHeight, getSuggestedMinimumHeight());
-        }
-
-        return measuredHeight;
-    }
-
-    @Override
-    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
-        super.onSizeChanged(w, h, oldw, oldh);
-        if (w != oldw || h != oldh) {
-            setup();
-        }
-    }
-
-    static private int ceil(double dimen) {
-        return (int) Math.ceil(dimen);
-    }
-
-    private void setup() {
-        if (mBackWidth == 0 || mBackHeight == 0) {
-            return;
-        }
-
-        mThumbRadius = mThumbSize / 2f;
-        mBackRadius = Math.min(mBackWidth, mBackHeight) / 2f;
-
-        int contentWidth = getMeasuredWidth() - getPaddingLeft() - getPaddingRight();
-        int contentHeight = getMeasuredHeight() - getPaddingTop() - getPaddingBottom();
-
-        // max range of drawing content, when thumbMargin is negative, drawing range is larger than backWidth
-        int drawingWidth = mBackWidth;
-        int drawingHeight = mBackHeight;
-
-        float thumbTop;
-        if (contentHeight <= drawingHeight) {
-            thumbTop = getPaddingTop() + mThumbMargin.top;
-        } else {
-            // center vertical in content area
-            thumbTop = getPaddingTop() + mThumbMargin.top + (contentHeight - drawingHeight + 1) / 2f;
-        }
-
-        float thumbLeft;
-        if (contentWidth <= mBackWidth) {
-            thumbLeft = getPaddingLeft() + mThumbMargin.left;
-        } else {
-            thumbLeft = getPaddingLeft() + mThumbMargin.left + (contentWidth - drawingWidth + 1) / 2f;
-        }
-        mThumbPos.set(thumbLeft + mThumbRadius, thumbTop + mThumbRadius);
-
-        float backLeft = thumbLeft - mThumbMargin.left;
-        mBackRectF.set(backLeft,
-                thumbTop - mThumbMargin.top,
-                backLeft + mBackWidth,
-                thumbTop - mThumbMargin.top + mBackHeight);
-
-        mSafeRectF.set(thumbLeft, 0, mBackRectF.right - mThumbMargin.right - mThumbSize, 0);
-
-        float minBackRadius = Math.min(mBackRectF.width(), mBackRectF.height()) / 2.f;
-        mBackRadius = Math.min(minBackRadius, mBackRadius);
-
-        if (mOnLayout != null) {
-            float onLeft = mBackRectF.left + (mBackRectF.width() - mThumbSize - mThumbMargin.right - mOnLayout.getWidth()) / 2f;
-            float onTop = mBackRectF.top + (mBackRectF.height() - mOnLayout.getHeight()) / 2;
-            mTextOnRectF.set(onLeft, onTop, onLeft + mOnLayout.getWidth(), onTop + mOnLayout.getHeight());
-        }
-
-        if (mOffLayout != null) {
-            float offLeft = mBackRectF.right - (mBackRectF.width() - mThumbSize - mThumbMargin.left - mOffLayout.getWidth()) / 2f - mOffLayout.getWidth();
-            float offTop = mBackRectF.top + (mBackRectF.height() - mOffLayout.getHeight()) / 2;
-            mTextOffRectF.set(offLeft, offTop, offLeft + mOffLayout.getWidth(), offTop + mOffLayout.getHeight());
-        }
-
-        int dWidth = mImageDrawable.getIntrinsicWidth();
-        int dHeight = mImageDrawable.getIntrinsicHeight();
-        int dTop = ceil(mBackRectF.top + (mBackRectF.height() - dHeight) / 2);
-        mImageDrawable.setBounds((int) ((mBackWidth - mThumbSize) / 2 - dWidth / 2), dTop, (int) ((mBackWidth - mThumbSize) / 2 + dWidth / 2), dTop + dHeight);
-
-        mReady = true;
-    }
-
-    @Override
-    protected void onDraw(Canvas canvas) {
-        super.onDraw(canvas);
-
-        if (!mReady) {
-            setup();
-        }
-        if (!mReady) {
-            return;
-        }
-
-        mPaint.setColor(mBackColor);
-        canvas.drawRoundRect(mBackRectF, mBackRadius, mBackRadius, mPaint);
-
-        // thumb
-        mPresentThumbPos.set(mThumbPos);
-        mPresentThumbPos.offset(mProgress * mSafeRectF.width(), 0);
-        mPaint.setColor(Color.WHITE);
-        canvas.drawCircle(mPresentThumbPos.x, mPresentThumbPos.y, mThumbRadius, mPaint);
-
-        // image
-        if (mShowImage) {
-            mImageDrawable.draw(canvas);
-        } else {
-            // text
-            Layout switchText = getProgress() > 0.5 ? mOnLayout : mOffLayout;
-            RectF textRectF = getProgress() > 0.5 ? mTextOnRectF : mTextOffRectF;
-            if (switchText != null && textRectF != null) {
-                float alpha = getProgress() >= 0.75 ? getProgress() * 4 - 3 : (getProgress() < 0.25 ? 1 - getProgress() * 4 : 0);
-                switchText.getPaint().setAlpha((int) (Color.alpha(getCurrentTextColor()) * alpha));
-                canvas.save();
-                canvas.translate(textRectF.left, textRectF.top);
-                switchText.draw(canvas);
-                canvas.restore();
-            }
-        }
-    }
-
-    @Override
-    public boolean onTouchEvent(MotionEvent event) {
-        if (!isEnabled() || !isClickable() || !isFocusable() || !mReady) {
-            return false;
-        }
-
-        int action = event.getAction();
-
-        float deltaX = event.getX() - mStartX;
-        float deltaY = event.getY() - mStartY;
-
-        switch (action) {
-            case MotionEvent.ACTION_DOWN:
-                mStartX = event.getX();
-                mStartY = event.getY();
-                mLastX = mStartX;
-                setPressed(true);
-                break;
-
-            case MotionEvent.ACTION_MOVE:
-                float x = event.getX();
-                setProgress(getProgress() + (x - mLastX) / mSafeRectF.width());
-                mLastX = x;
-                if (!mCatch && (Math.abs(deltaX) > mTouchSlop / 2f || Math.abs(deltaY) > mTouchSlop / 2f)) {
-                    if (deltaY == 0 || Math.abs(deltaX) > Math.abs(deltaY)) {
-                        catchView();
-                    } else if (Math.abs(deltaY) > Math.abs(deltaX)) {
-                        return false;
-                    }
-                }
-                break;
-
-            case MotionEvent.ACTION_CANCEL:
-            case MotionEvent.ACTION_UP:
-                mCatch = false;
-                setPressed(false);
-                float time = event.getEventTime() - event.getDownTime();
-                if (Math.abs(deltaX) < mTouchSlop && Math.abs(deltaY) < mTouchSlop && time < mClickTimeout) {
-                    performClick();
-                } else {
-                    boolean nextStatus = getProgress() > 0.5f;
-                    if (nextStatus != isChecked()) {
-                        playSoundEffect(SoundEffectConstants.CLICK);
-                        setChecked(nextStatus);
-                    } else {
-                        animateToState(nextStatus);
-                    }
-                }
-                break;
-
-            default:
-                break;
-        }
-        return true;
-    }
-
-    private float getProgress() {
-        return mProgress;
-    }
-
-    private void setProgress(final float progress) {
-        float tempProgress = progress;
-        if (tempProgress > 1) {
-            tempProgress = 1;
-        } else if (tempProgress < 0) {
-            tempProgress = 0;
-        }
-        mProgress = tempProgress;
-        invalidate();
-    }
-
-    @Override
-    public void setOnCheckedChangeListener(@Nullable OnCheckedChangeListener listener) {
-        mChildOnCheckedChangeListener = listener;
-    }
-
-    public void animateToState(boolean checked) {
-        if (mProgressAnimator == null) {
-            return;
-        }
-        if (mProgressAnimator.isRunning()) {
-            mProgressAnimator.cancel();
-        }
-        if (checked) {
-            mProgressAnimator.setFloatValues(mProgress, 1f);
-        } else {
-            mProgressAnimator.setFloatValues(mProgress, 0);
-        }
-        mProgressAnimator.start();
-    }
-
-    private void catchView() {
-        ViewParent parent = getParent();
-        if (parent != null) {
-            parent.requestDisallowInterceptTouchEvent(true);
-        }
-        mCatch = true;
-    }
-
-    @Override
-    public void setChecked(boolean checked) {
-        if (isChecked() == checked) {
-            return;
-        }
-        animateToState(checked);
-        super.setChecked(checked);
-    }
-
-    public void setCheckedSilent(boolean checked) {
-        mChangingState = true;
-        super.setChecked(checked);
-        setProgress(checked ? 1 : 0);
-        mChangingState = false;
-    }
-
-    public int getBackColor() {
-        return mBackColor;
-    }
-
-    public void setBackColor(int backColor) {
-        mBackColor = backColor;
-        invalidate();
-    }
-
-    public void setStatus(CharSequence status) {
-        mStatus = status;
-
-        mOnLayout = null;
-        mOffLayout = null;
-
-        mReady = false;
-        requestLayout();
-        invalidate();
-    }
-
-    public CharSequence getStatus() {
-        return mStatus;
-    }
-
-    public void showImage(boolean show) {
-        mShowImage = show;
-        invalidate();
-    }
-
-    public void startImageAnimation() {
-        ObjectAnimator anim = ObjectAnimator.ofInt(mImageDrawable, "level", 0, 10000);
-        anim.setDuration(500);
-        anim.setRepeatCount(ValueAnimator.INFINITE);
-        anim.start();
-    }
-
-}
diff --git a/ring-android/app/src/main/java/cx/ring/views/SwitchButton.kt b/ring-android/app/src/main/java/cx/ring/views/SwitchButton.kt
new file mode 100644
index 000000000..0b3f562fc
--- /dev/null
+++ b/ring-android/app/src/main/java/cx/ring/views/SwitchButton.kt
@@ -0,0 +1,406 @@
+/*
+ *  Copyright (C) 2004-2021 Savoir-faire Linux Inc.
+ *
+ *  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, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+package cx.ring.views
+
+import android.animation.ObjectAnimator
+import android.animation.ValueAnimator
+import android.content.Context
+import android.graphics.*
+import android.graphics.drawable.LayerDrawable
+import android.graphics.drawable.RotateDrawable
+import android.os.Build
+import android.text.Layout
+import android.text.StaticLayout
+import android.text.TextUtils
+import android.util.AttributeSet
+import android.util.TypedValue
+import android.view.MotionEvent
+import android.view.SoundEffectConstants
+import android.view.ViewConfiguration
+import android.view.animation.AccelerateDecelerateInterpolator
+import android.widget.CompoundButton
+import androidx.core.content.res.ResourcesCompat
+import cx.ring.R
+import kotlin.math.*
+
+class SwitchButton(context: Context, attrs: AttributeSet? = null, defStyle: Int = 0) : CompoundButton(context, attrs, defStyle) {
+    private var mBackColor: Int
+    private val mThumbSize: Int
+    private var mBackWidth = 0
+    private var mBackHeight = 0
+    private val mTouchSlop: Int = ViewConfiguration.get(context).scaledTouchSlop
+    private val mClickTimeout: Int = ViewConfiguration.getPressedStateDuration() + ViewConfiguration.getTapTimeout()
+    private var mThumbRadius = 0f
+    private var mBackRadius = 0f
+    private var mTextWidth = 0f
+    private var mTextHeight = 0f
+    private var mProgress = 0f
+    private var mStartX = 0f
+    private var mStartY = 0f
+    private var mLastX = 0f
+    private var mReady = false
+    private var mCatch = false
+    private var mShowImage = false
+    private val mThumbPos = PointF()
+    private val mPresentThumbPos = PointF()
+    private val mBackRectF = RectF()
+    private val mSafeRectF = RectF()
+    private val mTextOnRectF = RectF()
+    private val mTextOffRectF = RectF()
+    private val mThumbMargin = RectF()
+    private val mPaint = Paint(Paint.ANTI_ALIAS_FLAG)
+    private val mProgressAnimator = ValueAnimator.ofFloat(0f, 0f).apply {
+        interpolator = AccelerateDecelerateInterpolator()
+        addUpdateListener { valueAnimator -> progress = valueAnimator.animatedValue as Float }
+        duration = DEFAULT_ANIMATION_DURATION.toLong()
+    }
+    private var mStatus: CharSequence?
+    private var mOnLayout: Layout? = null
+    private var mOffLayout: Layout? = null
+    private val mImageDrawable: RotateDrawable
+    private var mChangingState = false
+    private var mChildOnCheckedChangeListener: OnCheckedChangeListener? = null
+
+    @JvmOverloads
+    constructor(context: Context, attrs: AttributeSet? = null) : this(context, attrs, 0)
+
+    private fun makeLayout(text: CharSequence?): Layout {
+        return StaticLayout(text, paint, ceil(Layout.getDesiredWidth(text, paint)).toInt(), Layout.Alignment.ALIGN_CENTER, 1f, 0f, false)
+    }
+
+    override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
+        if (mOnLayout == null && !TextUtils.isEmpty(mStatus)) {
+            mOnLayout = makeLayout(mStatus)
+        }
+        if (mOffLayout == null && !TextUtils.isEmpty(mStatus)) {
+            mOffLayout = makeLayout(mStatus)
+        }
+        val defaultWidth = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, DEFAULT_SWITCH_WIDTH.toFloat(), resources.displayMetrics)
+        mTextWidth = defaultWidth
+        val onHeight = if (mOnLayout != null) mOnLayout!!.height.toFloat() else 0f
+        val offHeight = if (mOffLayout != null) mOffLayout!!.height.toFloat() else 0f
+        mTextHeight = if (onHeight != 0f || offHeight != 0f) max(onHeight, offHeight) else 0f
+        setMeasuredDimension(measureWidth(widthMeasureSpec), measureHeight(heightMeasureSpec))
+    }
+
+    /**
+     * SwitchButton use this formula to determine the final size of thumb, background and itself.
+     *
+     *
+     * textWidth = max(onWidth, offWidth)
+     * thumbRange = thumbWidth * rangeRatio
+     * textExtraSpace = textWidth + textExtra - (moveRange - thumbWidth + max(thumbMargin.left, thumbMargin.right) + textThumbInset)
+     * backWidth = thumbRange + thumbMargin.left + thumbMargin.right + max(textExtraSpace, 0)
+     * contentSize = thumbRange + max(thumbMargin.left, 0) + max(thumbMargin.right, 0) + max(textExtraSpace, 0)
+     *
+     * @param widthMeasureSpec widthMeasureSpec
+     * @return measuredWidth
+     */
+    private fun measureWidth(widthMeasureSpec: Int): Int {
+        val widthSize = MeasureSpec.getSize(widthMeasureSpec)
+        val widthMode = MeasureSpec.getMode(widthMeasureSpec)
+        val measuredWidth: Int
+        if (widthMode == MeasureSpec.EXACTLY) {
+            measuredWidth = widthSize
+            mBackWidth = widthSize - paddingLeft - paddingRight
+        } else {
+            /*
+            If parent view want SwitchButton to determine it's size itself, we calculate the minimal
+            size of it's content. Further more, we ignore the limitation of widthSize since we want
+            to display SwitchButton in its actual size rather than compress the shape.
+             */
+            mBackWidth = max(ceil((mThumbSize + mThumbMargin.left + mThumbMargin.right + mTextWidth).toDouble()), 0)
+            measuredWidth = mBackWidth + paddingLeft + paddingRight
+        }
+        return measuredWidth
+    }
+
+    private fun measureHeight(heightMeasureSpec: Int): Int {
+        val heightSize = MeasureSpec.getSize(heightMeasureSpec)
+        val heightMode = MeasureSpec.getMode(heightMeasureSpec)
+        var measuredHeight = heightSize
+        val contentSize: Int
+        val textExtraSpace: Int
+        if (heightMode == MeasureSpec.EXACTLY) {
+            mBackHeight = ceil((mThumbSize + mThumbMargin.top + mThumbMargin.bottom)).toInt()
+            mBackHeight = ceil(max(mBackHeight.toFloat(), mTextHeight)).toInt()
+        } else {
+            mBackHeight = ceil((mThumbSize + mThumbMargin.top + mThumbMargin.bottom).toDouble())
+            textExtraSpace = ceil((mTextHeight - mBackHeight).toDouble())
+            if (textExtraSpace > 0) {
+                mBackHeight += textExtraSpace
+            }
+            contentSize = max(mThumbSize, mBackHeight)
+            measuredHeight = max(contentSize, contentSize + paddingTop + paddingBottom)
+            measuredHeight = max(measuredHeight, suggestedMinimumHeight)
+        }
+        return measuredHeight
+    }
+
+    override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
+        super.onSizeChanged(w, h, oldw, oldh)
+        if (w != oldw || h != oldh) {
+            setup()
+        }
+    }
+
+    private fun setup() {
+        if (mBackWidth == 0 || mBackHeight == 0) {
+            return
+        }
+        mThumbRadius = mThumbSize / 2f
+        mBackRadius = min(mBackWidth, mBackHeight) / 2f
+        val contentWidth = measuredWidth - paddingLeft - paddingRight
+        val contentHeight = measuredHeight - paddingTop - paddingBottom
+
+        // max range of drawing content, when thumbMargin is negative, drawing range is larger than backWidth
+        val drawingWidth = mBackWidth
+        val drawingHeight = mBackHeight
+        val thumbTop: Float = if (contentHeight <= drawingHeight) {
+            paddingTop + mThumbMargin.top
+        } else {
+            // center vertical in content area
+            paddingTop + mThumbMargin.top + (contentHeight - drawingHeight + 1) / 2f
+        }
+        val thumbLeft: Float = if (contentWidth <= mBackWidth) {
+            paddingLeft + mThumbMargin.left
+        } else {
+            paddingLeft + mThumbMargin.left + (contentWidth - drawingWidth + 1) / 2f
+        }
+        mThumbPos.set(thumbLeft + mThumbRadius, thumbTop + mThumbRadius)
+        val backLeft = thumbLeft - mThumbMargin.left
+        mBackRectF.set(backLeft, thumbTop - mThumbMargin.top, backLeft + mBackWidth, thumbTop - mThumbMargin.top + mBackHeight)
+        mSafeRectF.set(thumbLeft, 0f, mBackRectF.right - mThumbMargin.right - mThumbSize, 0f)
+        val minBackRadius = min(mBackRectF.width(), mBackRectF.height()) / 2f
+        mBackRadius = min(minBackRadius, mBackRadius)
+        mOnLayout?.let { onLayout ->
+            val onLeft = mBackRectF.left + (mBackRectF.width() - mThumbSize - mThumbMargin.right - onLayout.width) / 2f
+            val onTop = mBackRectF.top + (mBackRectF.height() - onLayout.height) / 2
+            mTextOnRectF.set(onLeft, onTop, onLeft + onLayout.width, onTop + onLayout.height)
+        }
+        mOffLayout?.let { offLayout ->
+            val offLeft = mBackRectF.right - (mBackRectF.width() - mThumbSize - mThumbMargin.left - offLayout.width) / 2f - offLayout.width
+            val offTop = mBackRectF.top + (mBackRectF.height() - offLayout.height) / 2
+            mTextOffRectF.set(offLeft, offTop, offLeft + offLayout.width, offTop + offLayout.height)
+        }
+        val dWidth = mImageDrawable.intrinsicWidth
+        val dHeight = mImageDrawable.intrinsicHeight
+        val dTop = ceil((mBackRectF.top + (mBackRectF.height() - dHeight) / 2).toDouble())
+        mImageDrawable.setBounds(
+            ((mBackWidth - mThumbSize) / 2 - dWidth / 2),
+            dTop,
+            ((mBackWidth - mThumbSize) / 2 + dWidth / 2),
+            dTop + dHeight
+        )
+        mReady = true
+    }
+
+    override fun onDraw(canvas: Canvas) {
+        super.onDraw(canvas)
+        if (!mReady) {
+            setup()
+        }
+        if (!mReady) {
+            return
+        }
+        mPaint.color = mBackColor
+        canvas.drawRoundRect(mBackRectF, mBackRadius, mBackRadius, mPaint)
+
+        // thumb
+        mPresentThumbPos.set(mThumbPos)
+        mPresentThumbPos.offset(mProgress * mSafeRectF.width(), 0f)
+        mPaint.color = Color.WHITE
+        canvas.drawCircle(mPresentThumbPos.x, mPresentThumbPos.y, mThumbRadius, mPaint)
+
+        // image
+        if (mShowImage) {
+            mImageDrawable.draw(canvas)
+        } else {
+            // text
+            val switchText = if (progress > 0.5) mOnLayout else mOffLayout
+            val textRectF = if (progress > 0.5) mTextOnRectF else mTextOffRectF
+            if (switchText != null) {
+                val alpha: Float =
+                    if (progress >= 0.75) progress * 4 - 3 else if (progress < 0.25) 1 - progress * 4 else 0f
+                switchText.paint.alpha = (Color.alpha(currentTextColor) * alpha).toInt()
+                canvas.save()
+                canvas.translate(textRectF.left, textRectF.top)
+                switchText.draw(canvas)
+                canvas.restore()
+            }
+        }
+    }
+
+    override fun onTouchEvent(event: MotionEvent): Boolean {
+        if (!isEnabled || !isClickable || !isFocusable || !mReady) {
+            return false
+        }
+        val action = event.action
+        val deltaX = event.x - mStartX
+        val deltaY = event.y - mStartY
+        when (action) {
+            MotionEvent.ACTION_DOWN -> {
+                mStartX = event.x
+                mStartY = event.y
+                mLastX = mStartX
+                isPressed = true
+            }
+            MotionEvent.ACTION_MOVE -> {
+                val x = event.x
+                progress += (x - mLastX) / mSafeRectF.width()
+                mLastX = x
+                if (!mCatch && (abs(deltaX) > mTouchSlop / 2f || abs(deltaY) > mTouchSlop / 2f)) {
+                    if (deltaY == 0f || abs(deltaX) > abs(deltaY)) {
+                        catchView()
+                    } else if (abs(deltaY) > abs(deltaX)) {
+                        return false
+                    }
+                }
+            }
+            MotionEvent.ACTION_CANCEL, MotionEvent.ACTION_UP -> {
+                mCatch = false
+                isPressed = false
+                val time = (event.eventTime - event.downTime).toFloat()
+                if (abs(deltaX) < mTouchSlop && abs(deltaY) < mTouchSlop && time < mClickTimeout) {
+                    performClick()
+                } else {
+                    val nextStatus = progress > 0.5f
+                    if (nextStatus != isChecked) {
+                        playSoundEffect(SoundEffectConstants.CLICK)
+                        isChecked = nextStatus
+                    } else {
+                        animateToState(nextStatus)
+                    }
+                }
+            }
+            else -> {
+            }
+        }
+        return true
+    }
+
+    private var progress: Float
+        get() = mProgress
+        set(progress) {
+            var tempProgress = progress
+            if (tempProgress > 1) {
+                tempProgress = 1f
+            } else if (tempProgress < 0) {
+                tempProgress = 0f
+            }
+            mProgress = tempProgress
+            invalidate()
+        }
+
+    override fun setOnCheckedChangeListener(listener: OnCheckedChangeListener?) {
+        mChildOnCheckedChangeListener = listener
+    }
+
+    private fun animateToState(checked: Boolean) {
+        if (mProgressAnimator.isRunning) {
+            mProgressAnimator.cancel()
+        }
+        mProgressAnimator.setFloatValues(mProgress, if (checked) 1f else 0f)
+        mProgressAnimator.start()
+    }
+
+    private fun catchView() {
+        val parent = parent
+        parent?.requestDisallowInterceptTouchEvent(true)
+        mCatch = true
+    }
+
+    override fun setChecked(checked: Boolean) {
+        if (isChecked == checked) {
+            return
+        }
+        animateToState(checked)
+        super.setChecked(checked)
+    }
+
+    fun setCheckedSilent(checked: Boolean) {
+        mChangingState = true
+        super.setChecked(checked)
+        progress = if (checked) 1f else 0f
+        mChangingState = false
+    }
+
+    var backColor: Int
+        get() = mBackColor
+        set(backColor) {
+            mBackColor = backColor
+            invalidate()
+        }
+    var status: CharSequence?
+        get() = mStatus
+        set(status) {
+            mStatus = status
+            mOnLayout = null
+            mOffLayout = null
+            mReady = false
+            requestLayout()
+            invalidate()
+        }
+
+    fun showImage(show: Boolean) {
+        mShowImage = show
+        invalidate()
+    }
+
+    fun startImageAnimation() {
+        val anim = ObjectAnimator.ofInt(mImageDrawable, "level", 0, 10000)
+        anim.duration = 500
+        anim.repeatCount = ValueAnimator.INFINITE
+        anim.start()
+    }
+
+    companion object {
+        const val DEFAULT_THUMB_SIZE_DP = 20
+        const val DEFAULT_THUMB_MARGIN_DP = 2
+        const val DEFAULT_ANIMATION_DURATION = 250
+        const val DEFAULT_SWITCH_WIDTH = 72
+        private fun ceil(dimen: Double): Int {
+            return kotlin.math.ceil(dimen).toInt()
+        }
+    }
+
+    init {
+        val ta = context.obtainStyledAttributes(attrs, R.styleable.SwitchButton)
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
+            saveAttributeDataForStyleable(context, R.styleable.SwitchButton, attrs, ta, defStyle, 0)
+        }
+        val backColor = ta.getColor(R.styleable.SwitchButton_backColor, resources.getColor(R.color.grey_400))
+        val status = ta.getString(R.styleable.SwitchButton_status)
+        ta.recycle()
+        val layerDrawable = ResourcesCompat.getDrawable(resources, R.drawable.rotate, context.theme) as LayerDrawable?
+        mImageDrawable = layerDrawable!!.findDrawableByLayerId(R.id.progress) as RotateDrawable
+        isFocusable = true
+        isClickable = true
+        mStatus = status
+        mBackColor = backColor
+        val margin = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, DEFAULT_THUMB_MARGIN_DP.toFloat(), resources.displayMetrics)
+        mThumbMargin.set(margin, margin, margin, margin)
+        mThumbSize = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, DEFAULT_THUMB_SIZE_DP.toFloat(), resources.displayMetrics).toInt()
+        // sync checked status
+        progress = if (isChecked) 1f else 0f
+        super.setOnCheckedChangeListener { buttonView: CompoundButton?, isChecked: Boolean ->
+            if (mChangingState) return@setOnCheckedChangeListener
+            mChildOnCheckedChangeListener?.onCheckedChanged(buttonView, isChecked)
+        }
+    }
+}
\ No newline at end of file
diff --git a/ring-android/build.gradle b/ring-android/build.gradle
index 7c4c426f8..86f96368d 100644
--- a/ring-android/build.gradle
+++ b/ring-android/build.gradle
@@ -4,10 +4,10 @@ buildscript {
         maven { url "https://maven.google.com" }
         mavenCentral()
     }
-    ext.kotlin_version = '1.5.21'
+    ext.kotlin_version = '1.5.30'
     ext.hilt_version = '2.38.1'
     dependencies {
-        classpath 'com.android.tools.build:gradle:7.0.0'
+        classpath 'com.android.tools.build:gradle:7.0.2'
         classpath 'com.google.gms:google-services:4.3.10'
         classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
         classpath "com.google.dagger:hilt-android-gradle-plugin:$hilt_version"
diff --git a/ring-android/libringclient/build.gradle b/ring-android/libringclient/build.gradle
index 9b10c1954..9807cb2b7 100644
--- a/ring-android/libringclient/build.gradle
+++ b/ring-android/libringclient/build.gradle
@@ -23,10 +23,10 @@ dependencies {
     testImplementation 'junit:junit:4.13.2'
 
     // RxJava
-    implementation 'io.reactivex.rxjava3:rxjava:3.0.13'
+    implementation 'io.reactivex.rxjava3:rxjava:3.1.1'
 
     // gson
-    implementation 'com.google.code.gson:gson:2.8.7'
+    implementation 'com.google.code.gson:gson:2.8.8'
 
     api "com.google.dagger:dagger:$hilt_version"
     kapt "com.google.dagger:dagger-compiler:$hilt_version"
diff --git a/ring-android/libringclient/src/main/java/net/jami/account/AccountWizardPresenter.java b/ring-android/libringclient/src/main/java/net/jami/account/AccountWizardPresenter.java
deleted file mode 100644
index dddac96d3..000000000
--- a/ring-android/libringclient/src/main/java/net/jami/account/AccountWizardPresenter.java
+++ /dev/null
@@ -1,306 +0,0 @@
-/*
- *  Copyright (C) 2004-2021 Savoir-faire Linux Inc.
- *
- *  Author: Hadrien De Sousa <hadrien.desousa@savoirfairelinux.com>
- *
- *  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, write to the Free Software
- *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-package net.jami.account;
-
-import java.util.HashMap;
-import java.util.Map;
-
-import javax.inject.Inject;
-import javax.inject.Named;
-
-import net.jami.model.Account;
-import net.jami.model.AccountConfig;
-import net.jami.model.ConfigKey;
-import net.jami.model.Settings;
-import net.jami.mvp.AccountCreationModel;
-import net.jami.mvp.RootPresenter;
-import net.jami.services.AccountService;
-import net.jami.services.DeviceRuntimeService;
-import net.jami.services.PreferencesService;
-import net.jami.utils.Log;
-import net.jami.utils.StringUtils;
-
-import io.reactivex.rxjava3.core.Observable;
-import io.reactivex.rxjava3.core.Scheduler;
-import io.reactivex.rxjava3.core.Single;
-import io.reactivex.rxjava3.subjects.BehaviorSubject;
-
-public class AccountWizardPresenter extends RootPresenter<net.jami.account.AccountWizardView> {
-
-    public static final String TAG = AccountWizardPresenter.class.getSimpleName();
-
-    private final AccountService mAccountService;
-    private final PreferencesService mPreferences;
-    private final DeviceRuntimeService mDeviceService;
-    private final Scheduler mUiScheduler;
-
-    //private boolean mCreationError = false;
-    private boolean mCreatingAccount = false;
-    private String mAccountType;
-    private AccountCreationModel mAccountCreationModel;
-
-    private Observable<Account> newAccount;
-
-    @Inject
-    public AccountWizardPresenter(AccountService accountService,
-                                  PreferencesService preferences,
-                                  DeviceRuntimeService deviceService,
-                                  @Named("UiScheduler") Scheduler uiScheduler) {
-        mAccountService = accountService;
-        mPreferences = preferences;
-        mDeviceService = deviceService;
-        mUiScheduler = uiScheduler;
-    }
-
-    public void init(String accountType) {
-        mAccountType = accountType;
-        if (AccountConfig.ACCOUNT_TYPE_SIP.equals(mAccountType)) {
-            getView().goToSipCreation();
-        } else {
-            getView().goToHomeCreation();
-        }
-    }
-
-    private void setProxyDetails(net.jami.mvp.AccountCreationModel accountCreationModel, Map<String, String> details)  {
-        if (accountCreationModel.isPush()) {
-            details.put(ConfigKey.PROXY_ENABLED.key(), AccountConfig.TRUE_STR);
-            String pushToken = mDeviceService.getPushToken();
-            if (!StringUtils.isEmpty(pushToken))
-                details.put(ConfigKey.PROXY_PUSH_TOKEN.key(), pushToken);
-        }
-    }
-
-    public void initJamiAccountConnect(AccountCreationModel accountCreationModel, String defaultAccountName) {
-        Single<Map<String, String>> newAccount = initRingAccountDetails(defaultAccountName)
-                .map(accountDetails -> {
-                    if (!StringUtils.isEmpty(accountCreationModel.getManagementServer())) {
-                        accountDetails.put(ConfigKey.MANAGER_URI.key(), accountCreationModel.getManagementServer());
-                        if (!StringUtils.isEmpty(accountCreationModel.getUsername())) {
-                            accountDetails.put(ConfigKey.MANAGER_USERNAME.key(), accountCreationModel.getUsername());
-                        }
-                    } else if (!StringUtils.isEmpty(accountCreationModel.getUsername())) {
-                        accountDetails.put(ConfigKey.ACCOUNT_USERNAME.key(), accountCreationModel.getUsername());
-                    }
-                    if (!StringUtils.isEmpty(accountCreationModel.getPassword())) {
-                        accountDetails.put(ConfigKey.ARCHIVE_PASSWORD.key(), accountCreationModel.getPassword());
-                    }
-                    setProxyDetails(accountCreationModel, accountDetails);
-                    return accountDetails;
-                });
-        createAccount(accountCreationModel, newAccount);
-    }
-
-    public void initJamiAccountCreation(net.jami.mvp.AccountCreationModel accountCreationModel, String defaultAccountName) {
-        Single<Map<String, String>> newAccount = initRingAccountDetails(defaultAccountName)
-                .map(accountDetails -> {
-                    if (!StringUtils.isEmpty(accountCreationModel.getUsername())) {
-                        accountDetails.put(ConfigKey.ACCOUNT_REGISTERED_NAME.key(), accountCreationModel.getUsername());
-                    }
-                    if (!StringUtils.isEmpty(accountCreationModel.getPassword())) {
-                        accountDetails.put(ConfigKey.ARCHIVE_PASSWORD.key(), accountCreationModel.getPassword());
-                    }
-                    setProxyDetails(accountCreationModel, accountDetails);
-                    return accountDetails;
-                });
-        createAccount(accountCreationModel, newAccount);
-    }
-
-    public void initJamiAccountLink(net.jami.mvp.AccountCreationModel accountCreationModel, String defaultAccountName) {
-        Single<Map<String, String>> newAccount = initRingAccountDetails(defaultAccountName)
-                .map(accountDetails -> {
-                    Settings settings = mPreferences.getSettings();
-                    if (settings != null && settings.isAllowPushNotifications()) {
-                        accountCreationModel.setPush(true);
-                        setProxyDetails(accountCreationModel, accountDetails);
-                    }
-                    if (!StringUtils.isEmpty(accountCreationModel.getPassword())) {
-                        accountDetails.put(ConfigKey.ARCHIVE_PASSWORD.key(), accountCreationModel.getPassword());
-                    }
-                    if (accountCreationModel.getArchive() != null) {
-                        accountDetails.put(ConfigKey.ARCHIVE_PATH.key(), accountCreationModel.getArchive().getAbsolutePath());
-                    } else if (!accountCreationModel.getPin().isEmpty()) {
-                        accountDetails.put(ConfigKey.ARCHIVE_PIN.key(), accountCreationModel.getPin());
-                    }
-                    return accountDetails;
-                });
-        createAccount(accountCreationModel, newAccount);
-    }
-
-    private void createAccount(AccountCreationModel accountCreationModel, Single<Map<String, String>> details) {
-        mAccountCreationModel = accountCreationModel;
-        Observable<Account> newAccount = details.flatMapObservable(accountDetails -> createNewAccount(accountCreationModel, accountDetails));
-        accountCreationModel.setAccountObservable(newAccount);
-        mCompositeDisposable.add(newAccount
-                .observeOn(mUiScheduler)
-                .subscribe(accountCreationModel::setNewAccount, e-> Log.e(TAG, "Can't create account", e)));
-        if (accountCreationModel.isLink()) {
-            getView().displayProgress(true);
-            mCompositeDisposable.add(newAccount
-                    .filter(a -> {
-                        String newState = a.getRegistrationState();
-                        return !(newState.isEmpty() || newState.contentEquals(AccountConfig.STATE_INITIALIZING));
-                    })
-                    .firstOrError()
-                    .observeOn(mUiScheduler)
-                    .subscribe(acc -> {
-                        accountCreationModel.setNewAccount(acc);
-                        net.jami.account.AccountWizardView view = getView();
-                        if (view != null) {
-                            view.displayProgress(false);
-                            String newState = acc.getRegistrationState();
-                            if (newState.contentEquals(AccountConfig.STATE_ERROR_GENERIC)) {
-                                mCreatingAccount = false;
-                                if (accountCreationModel.getArchive() == null)
-                                    view.displayCannotBeFoundError();
-                                else
-                                    view.displayGenericError();
-                            } else {
-                                view.goToProfileCreation(accountCreationModel);
-                            }
-                        }
-                    }, e -> {
-                        mCreatingAccount = false;
-                        getView().displayProgress(false);
-                        getView().displayCannotBeFoundError();
-                    }));
-        } else {
-            getView().goToProfileCreation(accountCreationModel);
-        }
-    }
-
-    public void successDialogClosed() {
-        net.jami.account.AccountWizardView view = getView();
-        if (view != null) {
-            getView().finish(true);
-        }
-    }
-
-    private Single<HashMap<String, String>> initRingAccountDetails(String defaultAccountName) {
-        return initAccountDetails().map(accountDetails -> {
-            accountDetails.put(ConfigKey.ACCOUNT_ALIAS.key(), mAccountService.getNewAccountName(defaultAccountName));
-            accountDetails.put(ConfigKey.ACCOUNT_UPNP_ENABLE.key(), AccountConfig.TRUE_STR);
-            return accountDetails;
-        });
-    }
-
-    private Single<HashMap<String, String>> initAccountDetails() {
-        if (mAccountType == null)
-            return Single.error(new IllegalStateException());
-        return mAccountService.getAccountTemplate(mAccountType)
-                .map(accountDetails -> {
-                    accountDetails.put(ConfigKey.VIDEO_ENABLED.key(), Boolean.toString(true));
-                    accountDetails.put(ConfigKey.ACCOUNT_DTMF_TYPE.key(), "sipinfo");
-                    return accountDetails;
-                });
-    }
-
-    private Observable<Account> createNewAccount(AccountCreationModel model, Map<String, String> accountDetails) {
-        if (mCreatingAccount) {
-            return newAccount;
-        }
-
-        mCreatingAccount = true;
-        //mCreationError = false;
-
-        BehaviorSubject<Account> account = BehaviorSubject.create();
-        account.filter(a -> {
-                    String newState = a.getRegistrationState();
-                    return !(newState.isEmpty() || newState.contentEquals(AccountConfig.STATE_INITIALIZING));
-                })
-                .firstElement()
-                .subscribe(a -> {
-                    if (!model.isLink() && a.isJami() && !StringUtils.isEmpty(model.getUsername()))
-                        mAccountService.registerName(a, model.getPassword(), model.getUsername());
-                    mAccountService.setCurrentAccount(a);
-                    if (model.isPush()) {
-                        Settings settings = mPreferences.getSettings();
-                        settings.setAllowPushNotifications(true);
-                        mPreferences.setSettings(settings);
-                    }
-                });
-
-        mAccountService
-                .addAccount(accountDetails)
-                .subscribe(account);
-
-        newAccount = account;
-        return account;
-    }
-
-    public void profileCreated(net.jami.mvp.AccountCreationModel accountCreationModel, boolean saveProfile) {
-        getView().blockOrientation();
-        getView().displayProgress(true);
-
-        Single<Account> newAccount = mAccountCreationModel
-                .getAccountObservable()
-                .filter(a -> {
-                    String newState = a.getRegistrationState();
-                    return !(newState.isEmpty() || newState.contentEquals(AccountConfig.STATE_INITIALIZING));
-                })
-                .firstOrError();
-
-        if (saveProfile) {
-            newAccount = newAccount.flatMap(a -> getView()
-                    .saveProfile(a, accountCreationModel)
-                    .map(vcard -> a));
-        }
-
-        mCompositeDisposable.add(newAccount
-                .observeOn(mUiScheduler)
-                .subscribe(account -> {
-                    mCreatingAccount = false;
-                    net.jami.account.AccountWizardView view = getView();
-                    if (view != null) {
-                        view.displayProgress(false);
-                        String newState = account.getRegistrationState();
-                        Log.w(TAG, "newState " + newState);
-                        switch (newState) {
-                            case AccountConfig.STATE_ERROR_GENERIC:
-                                view.displayGenericError();
-                                //mCreationError = true;
-                                break;
-                            case AccountConfig.STATE_UNREGISTERED:
-                                //mCreationError = false;
-                                break;
-                            case AccountConfig.STATE_ERROR_NETWORK:
-                                view.displayNetworkError();
-                                //mCreationError = true;
-                                break;
-                            default:
-                                //mCreationError = false;
-                                break;
-                        }
-                        view.displaySuccessDialog();
-                    }
-                }, e -> {
-                    mCreatingAccount = false;
-                    //mCreationError = true;
-                    AccountWizardView view = getView();
-                    if (view != null) {
-                        view.displayGenericError();
-                        getView().finish(true);
-                    }
-                }));
-    }
-
-    public void errorDialogClosed() {
-        getView().goToHomeCreation();
-    }
-}
diff --git a/ring-android/libringclient/src/main/java/net/jami/account/AccountWizardPresenter.kt b/ring-android/libringclient/src/main/java/net/jami/account/AccountWizardPresenter.kt
new file mode 100644
index 000000000..694518510
--- /dev/null
+++ b/ring-android/libringclient/src/main/java/net/jami/account/AccountWizardPresenter.kt
@@ -0,0 +1,265 @@
+/*
+ *  Copyright (C) 2004-2021 Savoir-faire Linux Inc.
+ *
+ *  Author: Hadrien De Sousa <hadrien.desousa@savoirfairelinux.com>
+ *
+ *  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, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+package net.jami.account
+
+import io.reactivex.rxjava3.core.Observable
+import io.reactivex.rxjava3.core.Scheduler
+import io.reactivex.rxjava3.core.Single
+import io.reactivex.rxjava3.subjects.BehaviorSubject
+import net.jami.model.Account
+import net.jami.model.AccountConfig
+import net.jami.model.ConfigKey
+import net.jami.model.AccountCreationModel
+import net.jami.mvp.RootPresenter
+import net.jami.services.AccountService
+import net.jami.services.DeviceRuntimeService
+import net.jami.services.PreferencesService
+import net.jami.utils.Log
+import net.jami.utils.StringUtils.isEmpty
+import java.util.*
+import javax.inject.Inject
+import javax.inject.Named
+
+class AccountWizardPresenter @Inject constructor(
+    private val mAccountService: AccountService,
+    private val mPreferences: PreferencesService,
+    private val mDeviceService: DeviceRuntimeService,
+    @param:Named("UiScheduler") private val mUiScheduler: Scheduler
+) : RootPresenter<AccountWizardView>() {
+    //private boolean mCreationError = false;
+    private var mCreatingAccount = false
+    private var mAccountType: String? = null
+    private var mAccountCreationModel: AccountCreationModel? = null
+    private var newAccount: Observable<Account>? = null
+
+    fun init(accountType: String) {
+        mAccountType = accountType
+        if (AccountConfig.ACCOUNT_TYPE_SIP == mAccountType) {
+            view?.goToSipCreation()
+        } else {
+            view?.goToHomeCreation()
+        }
+    }
+
+    private fun setProxyDetails(accountCreationModel: AccountCreationModel, details: MutableMap<String, String>) {
+        if (accountCreationModel.isPush) {
+            details[ConfigKey.PROXY_ENABLED.key()] = AccountConfig.TRUE_STR
+            val pushToken = mDeviceService.pushToken
+            if (pushToken != null && pushToken.isNotEmpty())
+                details[ConfigKey.PROXY_PUSH_TOKEN.key()] = pushToken
+        }
+    }
+
+    fun initJamiAccountConnect(accountCreationModel: AccountCreationModel, defaultAccountName: String) {
+        val newAccount = initRingAccountDetails(defaultAccountName)
+            .map<Map<String, String>> { accountDetails ->
+                if (!isEmpty(accountCreationModel.managementServer)) {
+                    accountDetails[ConfigKey.MANAGER_URI.key()] = accountCreationModel.managementServer!!
+                    if (!isEmpty(accountCreationModel.username)) {
+                        accountDetails[ConfigKey.MANAGER_USERNAME.key()] = accountCreationModel.username
+                    }
+                } else if (!isEmpty(accountCreationModel.username)) {
+                    accountDetails[ConfigKey.ACCOUNT_USERNAME.key()] = accountCreationModel.username
+                }
+                if (!isEmpty(accountCreationModel.password)) {
+                    accountDetails[ConfigKey.ARCHIVE_PASSWORD.key()] = accountCreationModel.password
+                }
+                setProxyDetails(accountCreationModel, accountDetails)
+                accountDetails
+            }
+        createAccount(accountCreationModel, newAccount)
+    }
+
+    fun initJamiAccountCreation(accountCreationModel: AccountCreationModel, defaultAccountName: String) {
+        val newAccount = initRingAccountDetails(defaultAccountName)
+            .map<Map<String, String>> { accountDetails ->
+                if (!isEmpty(accountCreationModel.username)) {
+                    accountDetails[ConfigKey.ACCOUNT_REGISTERED_NAME.key()] = accountCreationModel.username
+                }
+                if (!isEmpty(accountCreationModel.password)) {
+                    accountDetails[ConfigKey.ARCHIVE_PASSWORD.key()] = accountCreationModel.password
+                }
+                setProxyDetails(accountCreationModel, accountDetails)
+                accountDetails
+            }
+        createAccount(accountCreationModel, newAccount)
+    }
+
+    fun initJamiAccountLink(accountCreationModel: AccountCreationModel, defaultAccountName: String) {
+        val newAccount = initRingAccountDetails(defaultAccountName)
+            .map<Map<String, String>> { accountDetails ->
+                val settings = mPreferences.settings
+                if (settings.isAllowPushNotifications) {
+                    accountCreationModel.isPush = true
+                    setProxyDetails(accountCreationModel, accountDetails)
+                }
+                if (!isEmpty(accountCreationModel.password)) {
+                    accountDetails[ConfigKey.ARCHIVE_PASSWORD.key()] = accountCreationModel.password
+                }
+                if (accountCreationModel.archive != null) {
+                    accountDetails[ConfigKey.ARCHIVE_PATH.key()] = accountCreationModel.archive!!.absolutePath
+                } else if (accountCreationModel.pin.isNotEmpty()) {
+                    accountDetails[ConfigKey.ARCHIVE_PIN.key()] = accountCreationModel.pin
+                }
+                accountDetails
+            }
+        createAccount(accountCreationModel, newAccount)
+    }
+
+    private fun createAccount(accountCreationModel: AccountCreationModel, details: Single<Map<String, String>>) {
+        mAccountCreationModel = accountCreationModel
+        val newAccount = details.flatMapObservable { accountDetails -> createNewAccount(accountCreationModel, accountDetails) }
+        accountCreationModel.accountObservable = newAccount
+        mCompositeDisposable.add(newAccount
+            .observeOn(mUiScheduler)
+            .subscribe({ account: Account -> accountCreationModel.newAccount = account })
+            { e -> Log.e(TAG, "Can't create account", e) })
+        if (accountCreationModel.isLink) {
+            view!!.displayProgress(true)
+            mCompositeDisposable.add(newAccount
+                .filter { a: Account ->
+                    val newState = a.registrationState
+                    !(newState.isEmpty() || newState.contentEquals(AccountConfig.STATE_INITIALIZING))
+                }
+                .firstOrError()
+                .observeOn(mUiScheduler)
+                .subscribe({ acc: Account ->
+                    accountCreationModel.newAccount = acc
+                    val view = view
+                    if (view != null) {
+                        view.displayProgress(false)
+                        val newState = acc.registrationState
+                        if (newState.contentEquals(AccountConfig.STATE_ERROR_GENERIC)) {
+                            mCreatingAccount = false
+                            if (accountCreationModel.archive == null) view.displayCannotBeFoundError() else view.displayGenericError()
+                        } else {
+                            view.goToProfileCreation(accountCreationModel)
+                        }
+                    }
+                }) {
+                    mCreatingAccount = false
+                    view!!.displayProgress(false)
+                    view!!.displayCannotBeFoundError()
+                })
+        } else {
+            view?.goToProfileCreation(accountCreationModel)
+        }
+    }
+
+    fun successDialogClosed() {
+        view?.finish(true)
+    }
+
+    private fun initRingAccountDetails(defaultAccountName: String): Single<HashMap<String, String>> {
+        return initAccountDetails().map { accountDetails: HashMap<String, String> ->
+            accountDetails[ConfigKey.ACCOUNT_ALIAS.key()] = mAccountService.getNewAccountName(defaultAccountName)
+            accountDetails[ConfigKey.ACCOUNT_UPNP_ENABLE.key()] = AccountConfig.TRUE_STR
+            accountDetails
+        }
+    }
+
+    private fun initAccountDetails(): Single<HashMap<String, String>> {
+        return if (mAccountType == null) Single.error(IllegalStateException())
+        else mAccountService.getAccountTemplate(mAccountType!!)
+            .map { accountDetails: HashMap<String, String> ->
+                accountDetails[ConfigKey.VIDEO_ENABLED.key()] = true.toString()
+                accountDetails[ConfigKey.ACCOUNT_DTMF_TYPE.key()] = "sipinfo"
+                accountDetails
+            }
+    }
+
+    private fun createNewAccount(model: AccountCreationModel, accountDetails: Map<String, String>): Observable<Account> {
+        if (mCreatingAccount) {
+            return newAccount!!
+        }
+        mCreatingAccount = true
+        //mCreationError = false;
+        val account = BehaviorSubject.create<Account>()
+        account.filter { a: Account ->
+            val newState = a.registrationState
+            !(newState.isEmpty() || newState.contentEquals(AccountConfig.STATE_INITIALIZING))
+        }
+            .firstElement()
+            .subscribe { a: Account ->
+                if (!model.isLink && a.isJami && !isEmpty(model.username))
+                    mAccountService.registerName(a, model.password, model.username)
+                mAccountService.currentAccount = a
+                if (model.isPush) {
+                    val settings = mPreferences.settings
+                    settings.isAllowPushNotifications = true
+                    mPreferences.settings = settings
+                }
+            }
+        mAccountService.addAccount(accountDetails)
+            .subscribe(account)
+        newAccount = account
+        return account
+    }
+
+    fun profileCreated(accountCreationModel: AccountCreationModel, saveProfile: Boolean) {
+        view!!.blockOrientation()
+        view!!.displayProgress(true)
+        var newAccount = mAccountCreationModel!!.accountObservable!!.filter { a: Account ->
+                val newState = a.registrationState
+                !(newState.isEmpty() || newState.contentEquals(AccountConfig.STATE_INITIALIZING))
+            }
+            .firstOrError()
+        if (saveProfile) {
+            newAccount = newAccount.flatMap { a: Account ->
+                view!!.saveProfile(a, accountCreationModel).map { a }
+            }
+        }
+        mCompositeDisposable.add(newAccount
+            .observeOn(mUiScheduler)
+            .subscribe({ account: Account ->
+                mCreatingAccount = false
+                val view = view
+                if (view != null) {
+                    view.displayProgress(false)
+                    val newState = account.registrationState
+                    Log.w(TAG, "newState $newState")
+                    when (newState) {
+                        AccountConfig.STATE_ERROR_GENERIC -> view.displayGenericError()
+                        AccountConfig.STATE_UNREGISTERED -> { }
+                        AccountConfig.STATE_ERROR_NETWORK -> view.displayNetworkError()
+                        else -> {}
+                    }
+                    view.displaySuccessDialog()
+                }
+            }) { e ->
+                Log.e(TAG, "Error creating account", e);
+                mCreatingAccount = false
+                //mCreationError = true;
+                val view = view
+                if (view != null) {
+                    view.displayGenericError()
+                    //view.finish(true)
+                }
+            })
+    }
+
+    fun errorDialogClosed() {
+        view!!.goToHomeCreation()
+    }
+
+    companion object {
+        val TAG = AccountWizardPresenter::class.simpleName!!
+    }
+}
\ No newline at end of file
diff --git a/ring-android/libringclient/src/main/java/net/jami/account/AccountWizardView.java b/ring-android/libringclient/src/main/java/net/jami/account/AccountWizardView.java
deleted file mode 100644
index 7d927c1a5..000000000
--- a/ring-android/libringclient/src/main/java/net/jami/account/AccountWizardView.java
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- *  Copyright (C) 2004-2021 Savoir-faire Linux Inc.
- *
- *  Author: Hadrien De Sousa <hadrien.desousa@savoirfairelinux.com>
- *
- *  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, write to the Free Software
- *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-package net.jami.account;
-
-import net.jami.model.Account;
-import net.jami.mvp.AccountCreationModel;
-import ezvcard.VCard;
-import io.reactivex.rxjava3.core.Single;
-
-public interface AccountWizardView {
-
-    void goToHomeCreation();
-
-    void goToSipCreation();
-
-    void displayProgress(boolean display);
-
-    void displayCreationError();
-
-    void blockOrientation();
-
-    void finish(boolean affinity);
-
-    Single<VCard> saveProfile(Account account, AccountCreationModel accountCreationModel);
-
-    void displayGenericError();
-
-    void displayNetworkError();
-
-    void displayCannotBeFoundError();
-
-    void displaySuccessDialog();
-
-    void goToProfileCreation(AccountCreationModel accountCreationModel);
-}
\ No newline at end of file
diff --git a/ring-android/libringclient/src/main/java/net/jami/account/HomeAccountCreationPresenter.java b/ring-android/libringclient/src/main/java/net/jami/account/AccountWizardView.kt
similarity index 55%
rename from ring-android/libringclient/src/main/java/net/jami/account/HomeAccountCreationPresenter.java
rename to ring-android/libringclient/src/main/java/net/jami/account/AccountWizardView.kt
index 8995ca935..e1fd98db1 100644
--- a/ring-android/libringclient/src/main/java/net/jami/account/HomeAccountCreationPresenter.java
+++ b/ring-android/libringclient/src/main/java/net/jami/account/AccountWizardView.kt
@@ -17,28 +17,24 @@
  *  along with this program; if not, write to the Free Software
  *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
-
-package net.jami.account;
-
-import javax.inject.Inject;
-
-import net.jami.mvp.RootPresenter;
-
-public class HomeAccountCreationPresenter extends RootPresenter<HomeAccountCreationView> {
-
-    @Inject
-    public HomeAccountCreationPresenter() {
-    }
-
-    public void clickOnCreateAccount() {
-        getView().goToAccountCreation();
-    }
-
-    public void clickOnLinkAccount() {
-        getView().goToAccountLink();
-    }
-
-    public void clickOnConnectAccount() {
-        getView().goToAccountConnect();
-    }
-}
+package net.jami.account
+
+import ezvcard.VCard
+import io.reactivex.rxjava3.core.Single
+import net.jami.model.Account
+import net.jami.model.AccountCreationModel
+
+interface AccountWizardView {
+    fun goToHomeCreation()
+    fun goToSipCreation()
+    fun displayProgress(display: Boolean)
+    fun displayCreationError()
+    fun blockOrientation()
+    fun finish(affinity: Boolean)
+    fun saveProfile(account: Account, accountCreationModel: AccountCreationModel): Single<VCard>
+    fun displayGenericError()
+    fun displayNetworkError()
+    fun displayCannotBeFoundError()
+    fun displaySuccessDialog()
+    fun goToProfileCreation(accountCreationModel: AccountCreationModel)
+}
\ No newline at end of file
diff --git a/ring-android/app/src/main/java/cx/ring/fragments/GeneralAccountView.java b/ring-android/libringclient/src/main/java/net/jami/account/HomeAccountCreationPresenter.kt
similarity index 67%
rename from ring-android/app/src/main/java/cx/ring/fragments/GeneralAccountView.java
rename to ring-android/libringclient/src/main/java/net/jami/account/HomeAccountCreationPresenter.kt
index a05653cdf..bf632427d 100644
--- a/ring-android/app/src/main/java/cx/ring/fragments/GeneralAccountView.java
+++ b/ring-android/libringclient/src/main/java/net/jami/account/HomeAccountCreationPresenter.kt
@@ -17,22 +17,21 @@
  *  along with this program; if not, write to the Free Software
  *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
-package cx.ring.fragments;
+package net.jami.account
 
-import androidx.annotation.NonNull;
+import net.jami.mvp.RootPresenter
+import javax.inject.Inject
 
-import net.jami.model.Account;
-import net.jami.utils.Tuple;
+class HomeAccountCreationPresenter @Inject constructor() : RootPresenter<HomeAccountCreationView>() {
+    fun clickOnCreateAccount() {
+        view?.goToAccountCreation()
+    }
 
-public interface GeneralAccountView {
+    fun clickOnLinkAccount() {
+        view?.goToAccountLink()
+    }
 
-    void addJamiPreferences(String accountId);
-
-    void addSipPreferences();
-
-    void accountChanged(@NonNull Account account);
-
-    void finish();
-
-    void updateResolutions(Tuple<Integer, Integer> maxResolution, int currentResolution);
-}
+    fun clickOnConnectAccount() {
+        view?.goToAccountConnect()
+    }
+}
\ No newline at end of file
diff --git a/ring-android/libringclient/src/main/java/net/jami/model/Error.java b/ring-android/libringclient/src/main/java/net/jami/account/HomeAccountCreationView.kt
similarity index 83%
rename from ring-android/libringclient/src/main/java/net/jami/model/Error.java
rename to ring-android/libringclient/src/main/java/net/jami/account/HomeAccountCreationView.kt
index 5e7d2db3c..7114730bc 100644
--- a/ring-android/libringclient/src/main/java/net/jami/model/Error.java
+++ b/ring-android/libringclient/src/main/java/net/jami/account/HomeAccountCreationView.kt
@@ -1,5 +1,3 @@
-package net.jami.model;
-
 /*
  *  Copyright (C) 2004-2021 Savoir-faire Linux Inc.
  *
@@ -19,12 +17,10 @@ package net.jami.model;
  *  along with this program; if not, write to the Free Software
  *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
+package net.jami.account
 
-public enum Error {
-    GENERIC_ERROR,
-    NO_MICROPHONE,
-    NO_INPUT,
-    INVALID_FILE,
-    NOT_ABLE_TO_WRITE_FILE,
-    NO_SPACE_LEFT
-}
+interface HomeAccountCreationView {
+    fun goToAccountCreation()
+    fun goToAccountLink()
+    fun goToAccountConnect()
+}
\ No newline at end of file
diff --git a/ring-android/libringclient/src/main/java/net/jami/account/JamiAccountConnectPresenter.java b/ring-android/libringclient/src/main/java/net/jami/account/JamiAccountConnectPresenter.java
deleted file mode 100644
index 7038b2833..000000000
--- a/ring-android/libringclient/src/main/java/net/jami/account/JamiAccountConnectPresenter.java
+++ /dev/null
@@ -1,81 +0,0 @@
-/*
- *  Copyright (C) 2004-2021 Savoir-faire Linux Inc.
- *
- *  Author: Hadrien De Sousa <hadrien.desousa@savoirfairelinux.com>
- *
- *  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, write to the Free Software
- *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-package net.jami.account;
-
-import javax.inject.Inject;
-
-import net.jami.mvp.AccountCreationModel;
-import net.jami.mvp.RootPresenter;
-import net.jami.utils.StringUtils;
-
-public class JamiAccountConnectPresenter extends RootPresenter<JamiConnectAccountView> {
-
-    private AccountCreationModel mAccountCreationModel;
-
-    @Inject
-    public JamiAccountConnectPresenter() {
-    }
-
-    public void init(AccountCreationModel accountCreationModel) {
-        mAccountCreationModel = accountCreationModel;
-        if (mAccountCreationModel == null) {
-            getView().cancel();
-            return;
-        }
-        /*boolean hasArchive = mAccountCreationModel.getArchive() != null;
-        JamiConnectAccountView view = getView();
-        if (view != null) {
-            view.showPin(!hasArchive);
-            view.enableLinkButton(hasArchive);
-        }*/
-    }
-
-    public void passwordChanged(String password) {
-        mAccountCreationModel.setPassword(password);
-        showConnectButton();
-    }
-
-    public void usernameChanged(String username) {
-        mAccountCreationModel.setUsername(username);
-        showConnectButton();
-    }
-
-    public void serverChanged(String server) {
-        mAccountCreationModel.setManagementServer(server);
-        showConnectButton();
-    }
-
-    public void connectClicked() {
-        if (isFormValid()) {
-            getView().createAccount(mAccountCreationModel);
-        }
-    }
-
-    private void showConnectButton() {
-        getView().enableConnectButton(isFormValid());
-    }
-
-    private boolean isFormValid() {
-        return !StringUtils.isEmpty(mAccountCreationModel.getPassword())
-                && !StringUtils.isEmpty(mAccountCreationModel.getUsername())
-                && !StringUtils.isEmpty(mAccountCreationModel.getManagementServer());
-    }
-
-}
diff --git a/ring-android/libringclient/src/main/java/net/jami/account/JamiAccountConnectPresenter.kt b/ring-android/libringclient/src/main/java/net/jami/account/JamiAccountConnectPresenter.kt
new file mode 100644
index 000000000..67d26dd44
--- /dev/null
+++ b/ring-android/libringclient/src/main/java/net/jami/account/JamiAccountConnectPresenter.kt
@@ -0,0 +1,72 @@
+/*
+ *  Copyright (C) 2004-2021 Savoir-faire Linux Inc.
+ *
+ *  Author: Hadrien De Sousa <hadrien.desousa@savoirfairelinux.com>
+ *
+ *  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, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+package net.jami.account
+
+import net.jami.model.AccountCreationModel
+import net.jami.mvp.RootPresenter
+import net.jami.utils.StringUtils.isEmpty
+import javax.inject.Inject
+
+class JamiAccountConnectPresenter @Inject constructor() : RootPresenter<JamiConnectAccountView>() {
+    private var mAccountCreationModel: AccountCreationModel? = null
+    fun init(accountCreationModel: AccountCreationModel?) {
+        mAccountCreationModel = accountCreationModel
+        if (mAccountCreationModel == null) {
+            view?.cancel()
+            return
+        }
+        /*boolean hasArchive = mAccountCreationModel.getArchive() != null;
+        JamiConnectAccountView view = getView();
+        if (view != null) {
+            view.showPin(!hasArchive);
+            view.enableLinkButton(hasArchive);
+        }*/
+    }
+
+    fun passwordChanged(password: String) {
+        mAccountCreationModel!!.password = password
+        showConnectButton()
+    }
+
+    fun usernameChanged(username: String) {
+        mAccountCreationModel!!.username = username
+        showConnectButton()
+    }
+
+    fun serverChanged(server: String?) {
+        mAccountCreationModel!!.managementServer = server
+        showConnectButton()
+    }
+
+    fun connectClicked() {
+        if (isFormValid) {
+            view?.createAccount(mAccountCreationModel!!)
+        }
+    }
+
+    private fun showConnectButton() {
+        view?.enableConnectButton(isFormValid)
+    }
+
+    private val isFormValid: Boolean
+        get() = !isEmpty(mAccountCreationModel!!.password)
+                && !isEmpty(mAccountCreationModel!!.username)
+                && !isEmpty(mAccountCreationModel!!.managementServer)
+}
\ No newline at end of file
diff --git a/ring-android/libringclient/src/main/java/net/jami/account/JamiAccountCreationPresenter.kt b/ring-android/libringclient/src/main/java/net/jami/account/JamiAccountCreationPresenter.kt
index 0b5e640c9..6fabdd3a1 100644
--- a/ring-android/libringclient/src/main/java/net/jami/account/JamiAccountCreationPresenter.kt
+++ b/ring-android/libringclient/src/main/java/net/jami/account/JamiAccountCreationPresenter.kt
@@ -22,8 +22,7 @@ package net.jami.account
 
 import io.reactivex.rxjava3.core.Scheduler
 import io.reactivex.rxjava3.subjects.PublishSubject
-import net.jami.account.JamiAccountCreationPresenter
-import net.jami.mvp.AccountCreationModel
+import net.jami.model.AccountCreationModel
 import net.jami.mvp.RootPresenter
 import net.jami.services.AccountService
 import net.jami.services.AccountService.RegisteredName
@@ -49,7 +48,7 @@ class JamiAccountCreationPresenter @Inject constructor(
         super.bindView(view)
         mCompositeDisposable.add(contactQuery
             .debounce(TYPING_DELAY, TimeUnit.MILLISECONDS)
-            .switchMapSingle { q: String? -> mAccountService.findRegistrationByName("", "", q!!) }
+            .switchMapSingle { q: String -> mAccountService.findRegistrationByName("", "", q) }
             .observeOn(mUiScheduler)
             .subscribe { q: RegisteredName -> onLookupResult(q.name, q.address, q.state) })
     }
@@ -87,7 +86,7 @@ class JamiAccountCreationPresenter @Inject constructor(
     }
 
     fun passwordUnset() {
-        if (mAccountCreationModel != null) mAccountCreationModel!!.password = null
+        if (mAccountCreationModel != null) mAccountCreationModel!!.password = ""
         isPasswordCorrect = true
         isConfirmCorrect = true
         view?.showInvalidPasswordError(false)
@@ -102,62 +101,62 @@ class JamiAccountCreationPresenter @Inject constructor(
     fun passwordChanged(password: String) {
         if (mAccountCreationModel != null) mAccountCreationModel!!.password = password
         if (!isEmpty(password) && password.length < PASSWORD_MIN_LENGTH) {
-            view!!.showInvalidPasswordError(true)
+            view?.showInvalidPasswordError(true)
             isPasswordCorrect = false
         } else {
-            view!!.showInvalidPasswordError(false)
-            isPasswordCorrect = password.length != 0
+            view?.showInvalidPasswordError(false)
+            isPasswordCorrect = password.isNotEmpty()
             isConfirmCorrect = if (!password.contentEquals(mPasswordConfirm)) {
-                if (mPasswordConfirm.length > 0) view!!.showNonMatchingPasswordError(true)
+                if (mPasswordConfirm.isNotEmpty())
+                    view?.showNonMatchingPasswordError(true)
                 false
             } else {
-                view!!.showNonMatchingPasswordError(false)
+                view?.showNonMatchingPasswordError(false)
                 true
             }
         }
-        view!!.enableNextButton(isPasswordCorrect && isConfirmCorrect)
+        view?.enableNextButton(isPasswordCorrect && isConfirmCorrect)
     }
 
     fun passwordConfirmChanged(passwordConfirm: String) {
         isConfirmCorrect = if (passwordConfirm != mAccountCreationModel!!.password) {
-            view!!.showNonMatchingPasswordError(true)
+            view?.showNonMatchingPasswordError(true)
             false
         } else {
-            view!!.showNonMatchingPasswordError(false)
+            view?.showNonMatchingPasswordError(false)
             true
         }
         mPasswordConfirm = passwordConfirm
-        view!!.enableNextButton(isPasswordCorrect && isConfirmCorrect)
+        view?.enableNextButton(isPasswordCorrect && isConfirmCorrect)
     }
 
     fun createAccount() {
         if (isInputValid) {
-            val view = view
-            view!!.goToAccountCreation(mAccountCreationModel)
+            view?.goToAccountCreation(mAccountCreationModel!!)
         }
     }
 
     private val isInputValid: Boolean
-        private get() {
+        get() {
             val passwordOk = isPasswordCorrect && isConfirmCorrect
-            val usernameOk =
-                mAccountCreationModel != null && mAccountCreationModel!!.username != null || isUsernameCorrect
+            val usernameOk = mAccountCreationModel?.username != null || isUsernameCorrect
             return passwordOk && usernameOk
         }
 
     private fun checkForms() {
         val valid = isInputValid
-        if (valid && isUsernameCorrect) view!!.updateUsernameAvailability(JamiAccountCreationView.UsernameAvailabilityStatus.AVAILABLE)
+        if (valid && isUsernameCorrect)
+            view?.updateUsernameAvailability(JamiAccountCreationView.UsernameAvailabilityStatus.AVAILABLE)
     }
 
-    private fun onLookupResult(name: String?, address: String?, state: Int) {
+    private fun onLookupResult(name: String, address: String?, state: Int) {
         val view = view
         //Once we get the result, we can show the loading animation again when the user types
         showLoadingAnimation = true
         if (view == null) {
             return
         }
-        if (name == null || name.isEmpty()) {
+        if (name.isEmpty()) {
             view.updateUsernameAvailability(JamiAccountCreationView.UsernameAvailabilityStatus.RESET)
             isUsernameCorrect = false
         } else {
@@ -189,11 +188,11 @@ class JamiAccountCreationPresenter @Inject constructor(
     }
 
     fun setPush(push: Boolean) {
-        mAccountCreationModel!!.isPush = push
+        mAccountCreationModel?.isPush = push
     }
 
     companion object {
-        val TAG = JamiAccountCreationPresenter::class.java.simpleName
+        val TAG = JamiAccountCreationPresenter::class.simpleName!!
         private const val PASSWORD_MIN_LENGTH = 6
         private const val TYPING_DELAY = 350L
     }
diff --git a/ring-android/libringclient/src/main/java/net/jami/account/JamiAccountCreationView.kt b/ring-android/libringclient/src/main/java/net/jami/account/JamiAccountCreationView.kt
index 1bb996865..555eca5af 100644
--- a/ring-android/libringclient/src/main/java/net/jami/account/JamiAccountCreationView.kt
+++ b/ring-android/libringclient/src/main/java/net/jami/account/JamiAccountCreationView.kt
@@ -19,17 +19,17 @@
  */
 package net.jami.account
 
-import net.jami.mvp.AccountCreationModel
+import net.jami.model.AccountCreationModel
 
 interface JamiAccountCreationView {
     enum class UsernameAvailabilityStatus {
         ERROR_USERNAME_TAKEN, ERROR_USERNAME_INVALID, ERROR, LOADING, AVAILABLE, RESET
     }
 
-    fun updateUsernameAvailability(status: UsernameAvailabilityStatus?)
+    fun updateUsernameAvailability(status: UsernameAvailabilityStatus)
     fun showInvalidPasswordError(display: Boolean)
     fun showNonMatchingPasswordError(display: Boolean)
     fun enableNextButton(enabled: Boolean)
-    fun goToAccountCreation(accountCreationModel: AccountCreationModel?)
+    fun goToAccountCreation(accountCreationModel: AccountCreationModel)
     fun cancel()
 }
\ No newline at end of file
diff --git a/ring-android/libringclient/src/main/java/net/jami/account/JamiAccountSummaryPresenter.java b/ring-android/libringclient/src/main/java/net/jami/account/JamiAccountSummaryPresenter.java
deleted file mode 100644
index 7d76a0fde..000000000
--- a/ring-android/libringclient/src/main/java/net/jami/account/JamiAccountSummaryPresenter.java
+++ /dev/null
@@ -1,252 +0,0 @@
-/*
- *  Copyright (C) 2004-2021 Savoir-faire Linux Inc.
- *
- *  Author: Alexandre Lision <alexandre.lision@savoirfairelinux.com>
- *  Author: Adrien Béraud <adrien.beraud@savoirfairelinux.com>
- *
- *  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 net.jami.account;
-
-import net.jami.model.Account;
-import net.jami.services.AccountService;
-import net.jami.services.DeviceRuntimeService;
-
-import java.io.File;
-import java.net.SocketException;
-
-import javax.inject.Inject;
-import javax.inject.Named;
-
-import net.jami.mvp.RootPresenter;
-import net.jami.services.VCardService;
-import net.jami.utils.Log;
-import net.jami.utils.StringUtils;
-import net.jami.utils.VCardUtils;
-
-import ezvcard.property.Photo;
-import ezvcard.property.RawProperty;
-import ezvcard.property.Uid;
-import io.reactivex.rxjava3.core.Scheduler;
-import io.reactivex.rxjava3.core.Single;
-import io.reactivex.rxjava3.schedulers.Schedulers;
-
-public class JamiAccountSummaryPresenter extends RootPresenter<JamiAccountSummaryView> {
-
-    private static final String TAG = JamiAccountSummaryPresenter.class.getSimpleName();
-
-    private final DeviceRuntimeService mDeviceRuntimeService;
-    private final AccountService mAccountService;
-    private final VCardService mVcardService;
-
-    private String mAccountID;
-
-    @Inject
-    @Named("UiScheduler")
-    protected Scheduler mUiScheduler;
-
-    @Inject
-    public JamiAccountSummaryPresenter(AccountService accountService,
-                                       DeviceRuntimeService deviceRuntimeService,
-                                       VCardService vcardService) {
-        mAccountService = accountService;
-        mDeviceRuntimeService = deviceRuntimeService;
-        mVcardService = vcardService;
-    }
-
-    public void registerName(String name, String password) {
-        final Account account = mAccountService.getAccount(mAccountID);
-        if (account == null || getView() == null) {
-            return;
-        }
-        mAccountService.registerName(account, password, name);
-        getView().accountChanged(account);
-    }
-
-    public void startAccountExport(String password) {
-        if (getView() == null) {
-            return;
-        }
-        getView().showExportingProgressDialog();
-        mCompositeDisposable.add(mAccountService
-                .exportOnRing(mAccountID, password)
-                .observeOn(mUiScheduler)
-                .subscribe(pin -> getView().showPIN(pin),
-                           error -> {
-                    if (error instanceof IllegalArgumentException) {
-                        getView().showPasswordError();
-                    } else if (error instanceof SocketException) {
-                        getView().showNetworkError();
-                    } else {
-                        getView().showGenericError();
-                    }
-                }));
-    }
-
-    public void setAccountId(String accountID) {
-        mCompositeDisposable.clear();
-        mAccountID = accountID;
-        JamiAccountSummaryView v = getView();
-        Account account = mAccountService.getAccount(mAccountID);
-        if (v != null && account != null)
-            v.accountChanged(account);
-        mCompositeDisposable.add(mAccountService.getObservableAccountUpdates(mAccountID)
-                .observeOn(mUiScheduler)
-                .subscribe(a -> {
-                    JamiAccountSummaryView view = getView();
-                    if (view != null)
-                        view.accountChanged(a);
-                }));
-    }
-
-    public void enableAccount(boolean newValue) {
-        Account account = mAccountService.getAccount(mAccountID);
-        if (account == null) {
-            Log.w(TAG, "account not found!");
-            return;
-        }
-
-        account.setEnabled(newValue);
-        mAccountService.setAccountEnabled(account.getAccountID(), newValue);
-    }
-
-    public void changePassword(String oldPassword, String newPassword) {
-        JamiAccountSummaryView view = getView();
-        if (view != null)
-            view.showPasswordProgressDialog();
-        mCompositeDisposable.add(mAccountService.setAccountPassword(mAccountID, oldPassword, newPassword)
-                .observeOn(mUiScheduler)
-                .subscribe(
-                        () -> getView().passwordChangeEnded(true),
-                        e -> getView().passwordChangeEnded(false)));
-    }
-
-    public String getDeviceName() {
-        Account account = mAccountService.getAccount(mAccountID);
-        if (account == null) {
-            Log.w(TAG, "account not found!");
-            return null;
-        }
-        return account.getDeviceName();
-    }
-
-    public void downloadAccountsArchive(File dest, String password) {
-        getView().showExportingProgressDialog();
-        mCompositeDisposable.add(
-            mAccountService.exportToFile(mAccountID, dest.getAbsolutePath(), password)
-            .observeOn(mUiScheduler)
-            .subscribe(() -> getView().displayCompleteArchive(dest),
-                    error -> getView().passwordChangeEnded(false)));
-    }
-
-    public void saveVCardFormattedName(String username) {
-        Account account = mAccountService.getAccount(mAccountID);
-        File filesDir = mDeviceRuntimeService.provideFilesDir();
-
-        mCompositeDisposable.add(VCardUtils.loadLocalProfileFromDiskWithDefault(filesDir, mAccountID)
-                .doOnSuccess(vcard -> {
-                    vcard.setFormattedName(username);
-                    vcard.removeProperties(RawProperty.class);
-                })
-                .flatMap(vcard -> VCardUtils.saveLocalProfileToDisk(vcard, mAccountID, filesDir))
-                .subscribeOn(Schedulers.io())
-                .subscribe(vcard -> {
-                    account.setLoadedProfile(mVcardService.loadVCardProfile(vcard).cache());
-                }, e -> Log.e(TAG, "Error saving vCard !", e)));
-    }
-
-    public void saveVCard(String username, Single<Photo> photo) {
-        Account account = mAccountService.getAccount(mAccountID);
-        String ringId = account.getUsername();
-        File filesDir = mDeviceRuntimeService.provideFilesDir();
-        mCompositeDisposable.add(Single.zip(
-                VCardUtils.loadLocalProfileFromDiskWithDefault(filesDir, mAccountID).subscribeOn(Schedulers.io()),
-                photo, (vcard, pic) -> {
-                    vcard.setUid(new Uid(ringId));
-                    if (!StringUtils.isEmpty(username)) {
-                        vcard.setFormattedName(username);
-                    }
-                    vcard.removeProperties(Photo.class);
-                    vcard.addPhoto(pic);
-                    vcard.removeProperties(RawProperty.class);
-                    return vcard;
-                })
-                .flatMap(vcard -> VCardUtils.saveLocalProfileToDisk(vcard, mAccountID, filesDir))
-                .subscribeOn(Schedulers.io())
-                .subscribe(vcard -> {
-                    account.setLoadedProfile(mVcardService.loadVCardProfile(vcard).cache());
-                }, e -> Log.e(TAG, "Error saving vCard !", e)));
-    }
-
-    public void cameraClicked() {
-        boolean hasPermission = mDeviceRuntimeService.hasVideoPermission() &&
-                mDeviceRuntimeService.hasWriteExternalStoragePermission();
-        net.jami.account.JamiAccountSummaryView view = getView();
-        if (view != null) {
-            if (hasPermission) {
-                view.gotToImageCapture();
-            } else {
-                view.askCameraPermission();
-            }
-        }
-    }
-
-    public void galleryClicked() {
-        boolean hasPermission = mDeviceRuntimeService.hasGalleryPermission();
-        if (hasPermission) {
-            getView().goToGallery();
-        } else {
-            getView().askGalleryPermission();
-        }
-    }
-
-    public void goToAccount() {
-        getView().goToAccount(mAccountID);
-    }
-
-    public void goToMedia() {
-        getView().goToMedia(mAccountID);
-    }
-
-    public void goToSystem() {
-        getView().goToSystem(mAccountID);
-    }
-
-    public void goToAdvanced() {
-        getView().goToAdvanced(mAccountID);
-    }
-
-    public void revokeDevice(final String deviceId, String password) {
-        if (getView() != null) {
-            getView().showRevokingProgressDialog();
-        }
-        mCompositeDisposable.add(mAccountService
-                .revokeDevice(mAccountID, password, deviceId)
-                .observeOn(mUiScheduler)
-                .subscribe(result -> {
-                    getView().deviceRevocationEnded(deviceId, result);
-                    getView().updateDeviceList(mAccountService.getAccount(mAccountID).getDevices(),
-                            mAccountService.getAccount(mAccountID).getDeviceId());
-                }));
-    }
-
-    public void renameDevice(String newName) {
-        mAccountService.renameDevice(mAccountID, newName);
-    }
-
-    public Account getAccount() {
-        return mAccountService.getAccount(mAccountID);
-    }
-}
diff --git a/ring-android/libringclient/src/main/java/net/jami/account/JamiAccountSummaryPresenter.kt b/ring-android/libringclient/src/main/java/net/jami/account/JamiAccountSummaryPresenter.kt
new file mode 100644
index 000000000..e74d225b6
--- /dev/null
+++ b/ring-android/libringclient/src/main/java/net/jami/account/JamiAccountSummaryPresenter.kt
@@ -0,0 +1,218 @@
+/*
+ *  Copyright (C) 2004-2021 Savoir-faire Linux Inc.
+ *
+ *  Author: Alexandre Lision <alexandre.lision@savoirfairelinux.com>
+ *  Author: Adrien Béraud <adrien.beraud@savoirfairelinux.com>
+ *
+ *  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 net.jami.account
+
+import ezvcard.VCard
+import ezvcard.property.Photo
+import ezvcard.property.RawProperty
+import ezvcard.property.Uid
+import io.reactivex.rxjava3.core.Scheduler
+import io.reactivex.rxjava3.core.Single
+import io.reactivex.rxjava3.schedulers.Schedulers
+import net.jami.model.Account
+import net.jami.mvp.RootPresenter
+import net.jami.services.AccountService
+import net.jami.services.DeviceRuntimeService
+import net.jami.services.VCardService
+import net.jami.utils.Log
+import net.jami.utils.StringUtils
+import net.jami.utils.VCardUtils
+import java.io.File
+import java.net.SocketException
+import javax.inject.Inject
+import javax.inject.Named
+
+class JamiAccountSummaryPresenter @Inject constructor(
+    private val mAccountService: AccountService,
+    private val mDeviceRuntimeService: DeviceRuntimeService,
+    private val mVcardService: VCardService,
+    @param:Named("UiScheduler") private val mUiScheduler: Scheduler
+) : RootPresenter<JamiAccountSummaryView>() {
+    private var mAccountID: String? = null
+
+    fun registerName(name: String?, password: String?) {
+        val account = mAccountService.getAccount(mAccountID) ?: return
+        mAccountService.registerName(account, password, name)
+        //view?.accountChanged(account, a.second)
+    }
+
+    fun startAccountExport(password: String?) {
+        if (view == null || mAccountID == null) {
+            return
+        }
+        view?.showExportingProgressDialog()
+        mCompositeDisposable.add(mAccountService
+            .exportOnRing(mAccountID!!, password!!)
+            .observeOn(mUiScheduler)
+            .subscribe({ pin: String -> view?.showPIN(pin) }) { error: Throwable ->
+                when (error) {
+                    is IllegalArgumentException -> view?.showPasswordError()
+                    is SocketException -> view?.showNetworkError()
+                    else -> view?.showGenericError()
+                }
+            })
+    }
+
+    fun setAccountId(accountId: String) {
+        mCompositeDisposable.clear()
+        mAccountID = accountId
+        mCompositeDisposable.add(mAccountService.getObservableAccountProfile(accountId)
+            .observeOn(mUiScheduler)
+            .subscribe { a -> view?.accountChanged(a.first, a.second) })
+    }
+
+    fun enableAccount(newValue: Boolean) {
+        val account = mAccountService.getAccount(mAccountID)
+        if (account == null) {
+            Log.w(TAG, "account not found!")
+            return
+        }
+        account.isEnabled = newValue
+        mAccountService.setAccountEnabled(account.accountID, newValue)
+    }
+
+    fun changePassword(oldPassword: String, newPassword: String) {
+        view?.showPasswordProgressDialog()
+        mCompositeDisposable.add(mAccountService.setAccountPassword(mAccountID!!, oldPassword, newPassword)
+            .observeOn(mUiScheduler)
+            .subscribe({ view?.passwordChangeEnded(true) })
+            { view?.passwordChangeEnded(false) })
+    }
+
+    val deviceName: String?
+        get() {
+            val account = mAccountService.getAccount(mAccountID)
+            if (account == null) {
+                Log.w(TAG, "account not found!")
+                return null
+            }
+            return account.deviceName
+        }
+
+    fun downloadAccountsArchive(dest: File, password: String?) {
+        view?.showExportingProgressDialog()
+        mCompositeDisposable.add(
+            mAccountService.exportToFile(mAccountID!!, dest.absolutePath, password!!)
+                .observeOn(mUiScheduler)
+                .subscribe({ view?.displayCompleteArchive(dest) })
+                { view?.passwordChangeEnded(false) })
+    }
+
+    fun saveVCardFormattedName(username: String?) {
+        val accountId = mAccountID ?: return
+        val account = mAccountService.getAccount(accountId)
+        val filesDir = mDeviceRuntimeService.provideFilesDir()
+        mCompositeDisposable.add(VCardUtils.loadLocalProfileFromDiskWithDefault(filesDir, accountId)
+            .doOnSuccess { vcard: VCard ->
+                val previousName = vcard.formattedName?.value
+                if (StringUtils.isEmpty(previousName) == StringUtils.isEmpty(username) || previousName == username)
+                    throw IllegalArgumentException("Name didn't change")
+                vcard.setFormattedName(username)
+                vcard.removeProperties(RawProperty::class.java)
+                account?.loadedProfile = mVcardService.loadVCardProfile(vcard).cache()
+            }
+            .flatMap { vcard: VCard -> VCardUtils.saveLocalProfileToDisk(vcard, accountId, filesDir) }
+            .subscribeOn(Schedulers.io())
+            .subscribe({}) { e: Throwable -> Log.e(TAG, "Error saving vCard " + e.message) })
+    }
+
+    fun saveVCard(username: String?, photo: Single<Photo>) {
+        val accountId = mAccountID ?: return
+        val account = mAccountService.getAccount(accountId)!!
+        val ringId = account.username
+        val filesDir = mDeviceRuntimeService.provideFilesDir()
+        mCompositeDisposable.add(Single.zip(
+            VCardUtils.loadLocalProfileFromDiskWithDefault(filesDir, accountId).subscribeOn(Schedulers.io()),
+            photo, { vcard: VCard, pic: Photo ->
+                vcard.uid = Uid(ringId)
+                if (!StringUtils.isEmpty(username)) {
+                    vcard.setFormattedName(username)
+                }
+                vcard.removeProperties(Photo::class.java)
+                vcard.addPhoto(pic)
+                vcard.removeProperties(RawProperty::class.java)
+                vcard
+            })
+            .flatMap { vcard: VCard -> VCardUtils.saveLocalProfileToDisk(vcard, accountId, filesDir) }
+            .subscribeOn(Schedulers.io())
+            .subscribe({ vcard: VCard -> account.loadedProfile = mVcardService.loadVCardProfile(vcard).cache() })
+            { e: Throwable -> Log.e(TAG, "Error saving vCard !", e) })
+    }
+
+    fun cameraClicked() {
+        val hasPermission = mDeviceRuntimeService.hasVideoPermission() && mDeviceRuntimeService.hasWriteExternalStoragePermission()
+        val view = view
+        if (view != null) {
+            if (hasPermission) {
+                view.gotToImageCapture()
+            } else {
+                view.askCameraPermission()
+            }
+        }
+    }
+
+    fun galleryClicked() {
+        val hasPermission = mDeviceRuntimeService.hasGalleryPermission()
+        if (hasPermission) {
+            view!!.goToGallery()
+        } else {
+            view!!.askGalleryPermission()
+        }
+    }
+
+    fun goToAccount() {
+        view?.goToAccount(mAccountID!!)
+    }
+
+    fun goToMedia() {
+        view?.goToMedia(mAccountID!!)
+    }
+
+    fun goToSystem() {
+        view?.goToSystem(mAccountID!!)
+    }
+
+    fun goToAdvanced() {
+        view?.goToAdvanced(mAccountID!!)
+    }
+
+    fun revokeDevice(deviceId: String?, password: String?) {
+        view?.showRevokingProgressDialog()
+        mCompositeDisposable.add(mAccountService
+            .revokeDevice(mAccountID!!, password!!, deviceId!!)
+            .observeOn(mUiScheduler)
+            .subscribe { result: Int ->
+                val account = mAccountService.getAccount(mAccountID)!!
+                view?.deviceRevocationEnded(deviceId, result)
+                view?.updateDeviceList(account.devices, account.deviceId)
+            })
+    }
+
+    fun renameDevice(newName: String) {
+        mAccountService.renameDevice(mAccountID!!, newName)
+    }
+
+    val account: Account?
+        get() = mAccountService.getAccount(mAccountID)
+
+    companion object {
+        private val TAG = JamiAccountSummaryPresenter::class.simpleName!!
+    }
+}
\ No newline at end of file
diff --git a/ring-android/libringclient/src/main/java/net/jami/account/JamiAccountSummaryView.java b/ring-android/libringclient/src/main/java/net/jami/account/JamiAccountSummaryView.java
deleted file mode 100644
index 41c1d6b55..000000000
--- a/ring-android/libringclient/src/main/java/net/jami/account/JamiAccountSummaryView.java
+++ /dev/null
@@ -1,73 +0,0 @@
-/*
- *  Copyright (C) 2004-2021 Savoir-faire Linux Inc.
- *
- *  Author: Alexandre Lision <alexandre.lision@savoirfairelinux.com>
- *
- *  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 net.jami.account;
-
-import java.io.File;
-import java.util.Map;
-
-import net.jami.model.Account;
-
-public interface JamiAccountSummaryView {
-
-    void showExportingProgressDialog();
-
-    void showPasswordProgressDialog();
-
-    void accountChanged(final Account account);
-
-    void showNetworkError();
-
-    void showPasswordError();
-
-    void showGenericError();
-
-    void showPIN(String pin);
-
-    void passwordChangeEnded(boolean ok);
-
-    void displayCompleteArchive(File dest);
-
-    void gotToImageCapture();
-
-    void askCameraPermission();
-
-    void goToGallery();
-
-    void askGalleryPermission();
-
-    void updateUserView(Account account);
-
-    void goToMedia(String accountId);
-
-    void goToSystem(String accountId);
-
-    void goToAdvanced(String accountId);
-
-    void goToAccount(String accountId);
-
-    void setSwitchStatus(Account account);
-
-    void showRevokingProgressDialog();
-
-    void deviceRevocationEnded(String device, int status);
-
-    void updateDeviceList(Map<String, String> devices, String currentDeviceId);
-
-}
diff --git a/ring-android/libringclient/src/main/java/net/jami/account/JamiAccountSummaryView.kt b/ring-android/libringclient/src/main/java/net/jami/account/JamiAccountSummaryView.kt
new file mode 100644
index 000000000..443bdac94
--- /dev/null
+++ b/ring-android/libringclient/src/main/java/net/jami/account/JamiAccountSummaryView.kt
@@ -0,0 +1,48 @@
+/*
+ *  Copyright (C) 2004-2021 Savoir-faire Linux Inc.
+ *
+ *  Author: Alexandre Lision <alexandre.lision@savoirfairelinux.com>
+ *
+ *  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 net.jami.account
+
+import net.jami.model.Account
+import net.jami.model.Profile
+import java.io.File
+
+interface JamiAccountSummaryView {
+    fun showExportingProgressDialog()
+    fun showPasswordProgressDialog()
+    fun accountChanged(account: Account, profile: Profile)
+    fun showNetworkError()
+    fun showPasswordError()
+    fun showGenericError()
+    fun showPIN(pin: String)
+    fun passwordChangeEnded(ok: Boolean)
+    fun displayCompleteArchive(dest: File)
+    fun gotToImageCapture()
+    fun askCameraPermission()
+    fun goToGallery()
+    fun askGalleryPermission()
+    fun updateUserView(account: Account, profile: Profile)
+    fun goToMedia(accountId: String)
+    fun goToSystem(accountId: String)
+    fun goToAdvanced(accountId: String)
+    fun goToAccount(accountId: String)
+    fun setSwitchStatus(account: Account)
+    fun showRevokingProgressDialog()
+    fun deviceRevocationEnded(device: String, status: Int)
+    fun updateDeviceList(devices: Map<String, String>, currentDeviceId: String)
+}
\ No newline at end of file
diff --git a/ring-android/app/src/main/java/cx/ring/fragments/AdvancedAccountView.java b/ring-android/libringclient/src/main/java/net/jami/account/JamiConnectAccountView.kt
similarity index 78%
rename from ring-android/app/src/main/java/cx/ring/fragments/AdvancedAccountView.java
rename to ring-android/libringclient/src/main/java/net/jami/account/JamiConnectAccountView.kt
index e1649455d..98851bd07 100644
--- a/ring-android/app/src/main/java/cx/ring/fragments/AdvancedAccountView.java
+++ b/ring-android/libringclient/src/main/java/net/jami/account/JamiConnectAccountView.kt
@@ -17,14 +17,12 @@
  *  along with this program; if not, write to the Free Software
  *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
-package cx.ring.fragments;
+package net.jami.account
 
-import java.util.ArrayList;
+import net.jami.model.AccountCreationModel
 
-import net.jami.model.AccountConfig;
-
-public interface AdvancedAccountView {
-
-    void initView(AccountConfig config, ArrayList<CharSequence> networkInterfaces);
-
-}
+interface JamiConnectAccountView {
+    fun enableConnectButton(enable: Boolean)
+    fun createAccount(accountCreationModel: AccountCreationModel)
+    fun cancel()
+}
\ No newline at end of file
diff --git a/ring-android/libringclient/src/main/java/net/jami/account/JamiLinkAccountPresenter.java b/ring-android/libringclient/src/main/java/net/jami/account/JamiLinkAccountPresenter.java
deleted file mode 100644
index ac5a0746b..000000000
--- a/ring-android/libringclient/src/main/java/net/jami/account/JamiLinkAccountPresenter.java
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
- *  Copyright (C) 2004-2021 Savoir-faire Linux Inc.
- *
- *  Author: Hadrien De Sousa <hadrien.desousa@savoirfairelinux.com>
- *
- *  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, write to the Free Software
- *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-package net.jami.account;
-
-import javax.inject.Inject;
-
-import net.jami.mvp.AccountCreationModel;
-import net.jami.mvp.RootPresenter;
-
-public class JamiLinkAccountPresenter extends RootPresenter<net.jami.account.JamiLinkAccountView> {
-
-    private AccountCreationModel mAccountCreationModel;
-
-    @Inject
-    public JamiLinkAccountPresenter() {
-    }
-
-    public void init(AccountCreationModel accountCreationModel) {
-        mAccountCreationModel = accountCreationModel;
-        if (mAccountCreationModel == null) {
-            getView().cancel();
-            return;
-        }
-
-        boolean hasArchive = mAccountCreationModel.getArchive() != null;
-        JamiLinkAccountView view = getView();
-        if (view != null) {
-            view.showPin(!hasArchive);
-            view.enableLinkButton(hasArchive);
-        }
-    }
-
-    public void passwordChanged(String password) {
-        if (mAccountCreationModel != null)
-            mAccountCreationModel.setPassword(password);
-        showHideLinkButton();
-    }
-
-    public void pinChanged(String pin) {
-        if (mAccountCreationModel != null)
-            mAccountCreationModel.setPin(pin);
-        showHideLinkButton();
-    }
-
-    public void linkClicked() {
-        if (isFormValid()) {
-            getView().createAccount(mAccountCreationModel);
-        }
-    }
-
-    private void showHideLinkButton() {
-        getView().enableLinkButton(isFormValid());
-    }
-
-    private boolean isFormValid() {
-        return mAccountCreationModel.getArchive() != null || !mAccountCreationModel.getPin().isEmpty();
-    }
-}
diff --git a/ring-android/libringclient/src/main/java/net/jami/account/JamiLinkAccountPresenter.kt b/ring-android/libringclient/src/main/java/net/jami/account/JamiLinkAccountPresenter.kt
new file mode 100644
index 000000000..4253d2d73
--- /dev/null
+++ b/ring-android/libringclient/src/main/java/net/jami/account/JamiLinkAccountPresenter.kt
@@ -0,0 +1,65 @@
+/*
+ *  Copyright (C) 2004-2021 Savoir-faire Linux Inc.
+ *
+ *  Author: Hadrien De Sousa <hadrien.desousa@savoirfairelinux.com>
+ *
+ *  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, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+package net.jami.account
+
+import net.jami.model.AccountCreationModel
+import net.jami.mvp.RootPresenter
+import javax.inject.Inject
+
+class JamiLinkAccountPresenter @Inject constructor() : RootPresenter<JamiLinkAccountView>() {
+    private var mAccountCreationModel: AccountCreationModel? = null
+
+    fun init(accountCreationModel: AccountCreationModel?) {
+        mAccountCreationModel = accountCreationModel
+        if (mAccountCreationModel == null) {
+            view?.cancel()
+            return
+        }
+        val hasArchive = mAccountCreationModel?.archive != null
+        val view = view
+        if (view != null) {
+            view.showPin(!hasArchive)
+            view.enableLinkButton(hasArchive)
+        }
+    }
+
+    fun passwordChanged(password: String) {
+        mAccountCreationModel?.password = password
+        showHideLinkButton()
+    }
+
+    fun pinChanged(pin: String) {
+        mAccountCreationModel?.pin = pin
+        showHideLinkButton()
+    }
+
+    fun linkClicked() {
+        if (isFormValid) {
+            view?.createAccount(mAccountCreationModel!!)
+        }
+    }
+
+    private fun showHideLinkButton() {
+        view?.enableLinkButton(isFormValid)
+    }
+
+    private val isFormValid: Boolean
+        get() = mAccountCreationModel?.archive != null || mAccountCreationModel!!.pin.isNotEmpty()
+}
\ No newline at end of file
diff --git a/ring-android/libringclient/src/main/java/net/jami/account/JamiConnectAccountView.java b/ring-android/libringclient/src/main/java/net/jami/account/JamiLinkAccountView.kt
similarity index 76%
rename from ring-android/libringclient/src/main/java/net/jami/account/JamiConnectAccountView.java
rename to ring-android/libringclient/src/main/java/net/jami/account/JamiLinkAccountView.kt
index 1c07bf5f7..ccb856330 100644
--- a/ring-android/libringclient/src/main/java/net/jami/account/JamiConnectAccountView.java
+++ b/ring-android/libringclient/src/main/java/net/jami/account/JamiLinkAccountView.kt
@@ -17,15 +17,13 @@
  *  along with this program; if not, write to the Free Software
  *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
-package net.jami.account;
+package net.jami.account
 
-import net.jami.mvp.AccountCreationModel;
+import net.jami.model.AccountCreationModel
 
-public interface JamiConnectAccountView {
-
-    void enableConnectButton(boolean enable);
-
-    void createAccount(AccountCreationModel accountCreationModel);
-
-    void cancel();
-}
+interface JamiLinkAccountView {
+    fun enableLinkButton(enable: Boolean)
+    fun showPin(show: Boolean)
+    fun createAccount(accountCreationModel: AccountCreationModel)
+    fun cancel()
+}
\ No newline at end of file
diff --git a/ring-android/libringclient/src/main/java/net/jami/account/LinkDevicePresenter.java b/ring-android/libringclient/src/main/java/net/jami/account/LinkDevicePresenter.java
deleted file mode 100644
index 9c914ad71..000000000
--- a/ring-android/libringclient/src/main/java/net/jami/account/LinkDevicePresenter.java
+++ /dev/null
@@ -1,88 +0,0 @@
-/*
- *  Copyright (C) 2004-2021 Savoir-faire Linux Inc.
- *
- *  Author: Alexandre Lision <alexandre.lision@savoirfairelinux.com>
- *  Author: Adrien Béraud <adrien.beraud@savoirfairelinux.com>
- *
- *  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 net.jami.account;
-
-import net.jami.services.AccountService;
-
-import java.net.SocketException;
-
-import javax.inject.Inject;
-import javax.inject.Named;
-
-import net.jami.model.Account;
-import net.jami.mvp.RootPresenter;
-
-import io.reactivex.rxjava3.core.Scheduler;
-
-public class LinkDevicePresenter extends RootPresenter<LinkDeviceView> {
-
-    private static final String TAG = LinkDevicePresenter.class.getSimpleName();
-
-    private net.jami.services.AccountService mAccountService;
-    private String mAccountID;
-
-    @Inject
-    @Named("UiScheduler")
-    protected Scheduler mUiScheduler;
-
-    @Inject
-    public LinkDevicePresenter(AccountService accountService) {
-        mAccountService = accountService;
-    }
-
-    public void startAccountExport(String password) {
-        if (getView() == null) {
-            return;
-        }
-        getView().showExportingProgress();
-        mCompositeDisposable.add(mAccountService
-                .exportOnRing(mAccountID, password)
-                .observeOn(mUiScheduler)
-                .subscribe(pin -> getView().showPIN(pin),
-                           error -> {
-                    getView().dismissExportingProgress();
-                    if (error instanceof IllegalArgumentException) {
-                        getView().showPasswordError();
-                    } else if (error instanceof SocketException) {
-                        getView().showNetworkError();
-                    } else {
-                        getView().showGenericError();
-                    }
-                }));
-    }
-
-    public void setAccountId(String accountID) {
-        mCompositeDisposable.clear();
-        mAccountID = accountID;
-        LinkDeviceView v = getView();
-        Account account = mAccountService.getAccount(mAccountID);
-        if (v != null && account != null)
-            v.accountChanged(account);
-        mCompositeDisposable.add(mAccountService.getObservableAccountUpdates(mAccountID)
-                .observeOn(mUiScheduler)
-                .subscribe(a -> {
-                    LinkDeviceView view = getView();
-                    if (view != null)
-                        view.accountChanged(a);
-                }));
-    }
-
-}
diff --git a/ring-android/libringclient/src/main/java/net/jami/account/LinkDevicePresenter.kt b/ring-android/libringclient/src/main/java/net/jami/account/LinkDevicePresenter.kt
new file mode 100644
index 000000000..f8872bb92
--- /dev/null
+++ b/ring-android/libringclient/src/main/java/net/jami/account/LinkDevicePresenter.kt
@@ -0,0 +1,70 @@
+/*
+ *  Copyright (C) 2004-2021 Savoir-faire Linux Inc.
+ *
+ *  Author: Alexandre Lision <alexandre.lision@savoirfairelinux.com>
+ *  Author: Adrien Béraud <adrien.beraud@savoirfairelinux.com>
+ *
+ *  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 net.jami.account
+
+import io.reactivex.rxjava3.core.Scheduler
+import net.jami.model.Account
+import net.jami.mvp.RootPresenter
+import net.jami.services.AccountService
+import java.net.SocketException
+import javax.inject.Inject
+import javax.inject.Named
+
+class LinkDevicePresenter @Inject constructor(
+    private val mAccountService: AccountService,
+    @Named("UiScheduler")
+    private var mUiScheduler: Scheduler
+) : RootPresenter<LinkDeviceView>() {
+    private var mAccountID: String? = null
+
+    fun startAccountExport(password: String?) {
+        if (view == null) {
+            return
+        }
+        view?.showExportingProgress()
+        mCompositeDisposable.add(mAccountService
+            .exportOnRing(mAccountID!!, password!!)
+            .observeOn(mUiScheduler)
+            .subscribe({ pin: String -> view?.showPIN(pin) })
+            { error: Throwable ->
+                view?.dismissExportingProgress()
+                when (error) {
+                    is IllegalArgumentException -> view?.showPasswordError()
+                    is SocketException -> view?.showNetworkError()
+                    else -> view?.showGenericError()
+                }
+            })
+    }
+
+    fun setAccountId(accountID: String) {
+        mCompositeDisposable.clear()
+        mAccountID = accountID
+        val account = mAccountService.getAccount(accountID)
+        if (account != null)
+            view?.accountChanged(account)
+        mCompositeDisposable.add(mAccountService.getObservableAccountUpdates(accountID)
+            .observeOn(mUiScheduler)
+            .subscribe { a: Account -> view?.accountChanged(a) })
+    }
+
+    companion object {
+        private val TAG = LinkDevicePresenter::class.simpleName!!
+    }
+}
\ No newline at end of file
diff --git a/ring-android/libringclient/src/main/java/net/jami/account/LinkDeviceView.java b/ring-android/libringclient/src/main/java/net/jami/account/LinkDeviceView.kt
similarity index 70%
rename from ring-android/libringclient/src/main/java/net/jami/account/LinkDeviceView.java
rename to ring-android/libringclient/src/main/java/net/jami/account/LinkDeviceView.kt
index 120f2d028..9374717dc 100644
--- a/ring-android/libringclient/src/main/java/net/jami/account/LinkDeviceView.java
+++ b/ring-android/libringclient/src/main/java/net/jami/account/LinkDeviceView.kt
@@ -16,25 +16,16 @@
  *  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 net.jami.account;
-
-import net.jami.model.Account;
-
-public interface LinkDeviceView {
-
-    void showExportingProgress();
-
-    void dismissExportingProgress();
-
-    void accountChanged(final Account account);
-
-    void showNetworkError();
-
-    void showPasswordError();
-
-    void showGenericError();
-
-    void showPIN(String pin);
-
-}
+package net.jami.account
+
+import net.jami.model.Account
+
+interface LinkDeviceView {
+    fun showExportingProgress()
+    fun dismissExportingProgress()
+    fun accountChanged(account: Account?)
+    fun showNetworkError()
+    fun showPasswordError()
+    fun showGenericError()
+    fun showPIN(pin: String)
+}
\ No newline at end of file
diff --git a/ring-android/libringclient/src/main/java/net/jami/account/ProfileCreationPresenter.java b/ring-android/libringclient/src/main/java/net/jami/account/ProfileCreationPresenter.java
deleted file mode 100644
index ef03a12ce..000000000
--- a/ring-android/libringclient/src/main/java/net/jami/account/ProfileCreationPresenter.java
+++ /dev/null
@@ -1,120 +0,0 @@
-/*
- *  Copyright (C) 2004-2021 Savoir-faire Linux Inc.
- *
- *  Author: Hadrien De Sousa <hadrien.desousa@savoirfairelinux.com>
- *
- *  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, write to the Free Software
- *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-package net.jami.account;
-
-import net.jami.mvp.AccountCreationModel;
-
-import javax.inject.Inject;
-import javax.inject.Named;
-
-import net.jami.mvp.RootPresenter;
-import net.jami.services.DeviceRuntimeService;
-import net.jami.services.HardwareService;
-import net.jami.utils.Log;
-
-import io.reactivex.rxjava3.core.Scheduler;
-import io.reactivex.rxjava3.core.Single;
-
-public class ProfileCreationPresenter extends RootPresenter<net.jami.account.ProfileCreationView> {
-
-    public static final String TAG = ProfileCreationPresenter.class.getSimpleName();
-
-    private final DeviceRuntimeService mDeviceRuntimeService;
-    private final HardwareService mHardwareService;
-    private final Scheduler mUiScheduler;
-
-    private net.jami.mvp.AccountCreationModel mAccountCreationModel;
-
-    @Inject
-    public ProfileCreationPresenter(DeviceRuntimeService deviceRuntimeService,
-                                    HardwareService hardwareService,
-                                    @Named("UiScheduler") Scheduler uiScheduler) {
-        mDeviceRuntimeService = deviceRuntimeService;
-        mHardwareService = hardwareService;
-        mUiScheduler = uiScheduler;
-    }
-
-    public void initPresenter(AccountCreationModel accountCreationModel) {
-        Log.w(TAG, "initPresenter");
-        mAccountCreationModel = accountCreationModel;
-        if (mDeviceRuntimeService.hasContactPermission()) {
-            String profileName = mDeviceRuntimeService.getProfileName();
-            if (profileName != null) {
-                getView().displayProfileName(profileName);
-            }
-        } else {
-            Log.d(TAG, "READ_CONTACTS permission is not granted.");
-        }
-        mCompositeDisposable.add(accountCreationModel
-                .getProfileUpdates()
-                .observeOn(mUiScheduler)
-                .subscribe(model -> {
-                    ProfileCreationView view = getView();
-                    if (view != null)
-                        view.setProfile(model);
-                }));
-    }
-
-    public void fullNameUpdated(String fullName) {
-        if (mAccountCreationModel != null)
-            mAccountCreationModel.setFullName(fullName);
-    }
-
-    public void photoUpdated(Single<Object> bitmap) {
-        mCompositeDisposable.add(bitmap
-                .subscribe(b -> mAccountCreationModel.setPhoto(b),
-                           e -> Log.e(TAG, "Can't load image", e)));
-    }
-
-    public void galleryClick() {
-        boolean hasPermission = mDeviceRuntimeService.hasGalleryPermission();
-        if (hasPermission) {
-            getView().goToGallery();
-        } else {
-            getView().askStoragePermission();
-        }
-    }
-
-    public void cameraClick() {
-        boolean hasPermission = mDeviceRuntimeService.hasVideoPermission() &&
-                mDeviceRuntimeService.hasWriteExternalStoragePermission();
-        if (hasPermission) {
-            getView().goToPhotoCapture();
-        } else {
-            getView().askPhotoPermission();
-        }
-    }
-
-    public void cameraPermissionChanged(boolean isGranted) {
-        if (isGranted && mHardwareService.isVideoAvailable()) {
-            mHardwareService.initVideo()
-                    .onErrorComplete()
-                    .subscribe();
-        }
-    }
-
-    public void nextClick() {
-        getView().goToNext(mAccountCreationModel, true);
-    }
-
-    public void skipClick() {
-        getView().goToNext(mAccountCreationModel, false);
-    }
-}
diff --git a/ring-android/libringclient/src/main/java/net/jami/account/ProfileCreationPresenter.kt b/ring-android/libringclient/src/main/java/net/jami/account/ProfileCreationPresenter.kt
new file mode 100644
index 000000000..00e94eb7b
--- /dev/null
+++ b/ring-android/libringclient/src/main/java/net/jami/account/ProfileCreationPresenter.kt
@@ -0,0 +1,103 @@
+/*
+ *  Copyright (C) 2004-2021 Savoir-faire Linux Inc.
+ *
+ *  Author: Hadrien De Sousa <hadrien.desousa@savoirfairelinux.com>
+ *
+ *  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, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+package net.jami.account
+
+import io.reactivex.rxjava3.core.Scheduler
+import io.reactivex.rxjava3.core.Single
+import net.jami.model.AccountCreationModel
+import net.jami.mvp.RootPresenter
+import net.jami.services.DeviceRuntimeService
+import net.jami.services.HardwareService
+import net.jami.utils.Log
+import javax.inject.Inject
+import javax.inject.Named
+
+class ProfileCreationPresenter @Inject constructor(
+    private val mDeviceRuntimeService: DeviceRuntimeService,
+    private val mHardwareService: HardwareService,
+    @param:Named("UiScheduler") private val mUiScheduler: Scheduler
+) : RootPresenter<ProfileCreationView>() {
+    private var mAccountCreationModel: AccountCreationModel? = null
+
+    fun initPresenter(accountCreationModel: AccountCreationModel) {
+        Log.w(TAG, "initPresenter")
+        mAccountCreationModel = accountCreationModel
+        if (mDeviceRuntimeService.hasContactPermission()) {
+            val profileName = mDeviceRuntimeService.profileName
+            if (profileName != null)
+                view?.displayProfileName(profileName)
+        } else {
+            Log.d(TAG, "READ_CONTACTS permission is not granted.")
+        }
+        mCompositeDisposable.add(accountCreationModel
+            .profileUpdates
+            .observeOn(mUiScheduler)
+            .subscribe { model -> view?.setProfile(model) })
+    }
+
+    fun fullNameUpdated(fullName: String) {
+        mAccountCreationModel?.fullName = fullName
+    }
+
+    fun photoUpdated(bitmap: Single<Any>) {
+        mCompositeDisposable.add(bitmap
+            .subscribe({ b: Any -> mAccountCreationModel?.photo = b })
+            { e: Throwable -> Log.e(TAG, "Can't load image", e) })
+    }
+
+    fun galleryClick() {
+        val hasPermission = mDeviceRuntimeService.hasGalleryPermission()
+        if (hasPermission) {
+            view?.goToGallery()
+        } else {
+            view?.askStoragePermission()
+        }
+    }
+
+    fun cameraClick() {
+        val hasPermission = mDeviceRuntimeService.hasVideoPermission() &&
+                mDeviceRuntimeService.hasWriteExternalStoragePermission()
+        if (hasPermission) {
+            view?.goToPhotoCapture()
+        } else {
+            view?.askPhotoPermission()
+        }
+    }
+
+    fun cameraPermissionChanged(isGranted: Boolean) {
+        if (isGranted && mHardwareService.isVideoAvailable) {
+            mHardwareService.initVideo()
+                .onErrorComplete()
+                .subscribe()
+        }
+    }
+
+    fun nextClick() {
+        view?.goToNext(mAccountCreationModel!!, true)
+    }
+
+    fun skipClick() {
+        view?.goToNext(mAccountCreationModel!!, false)
+    }
+
+    companion object {
+        val TAG = ProfileCreationPresenter::class.simpleName!!
+    }
+}
\ No newline at end of file
diff --git a/ring-android/libringclient/src/main/java/net/jami/account/ProfileCreationView.java b/ring-android/libringclient/src/main/java/net/jami/account/ProfileCreationView.java
deleted file mode 100644
index 3a3aee450..000000000
--- a/ring-android/libringclient/src/main/java/net/jami/account/ProfileCreationView.java
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- *  Copyright (C) 2004-2021 Savoir-faire Linux Inc.
- *
- *  Author: Hadrien De Sousa <hadrien.desousa@savoirfairelinux.com>
- *
- *  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, write to the Free Software
- *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-package net.jami.account;
-
-import net.jami.mvp.AccountCreationModel;
-
-public interface ProfileCreationView {
-
-    void displayProfileName(String profileName);
-
-    void goToGallery();
-
-    void goToPhotoCapture();
-
-    void askStoragePermission();
-
-    void askPhotoPermission();
-
-    void goToNext(AccountCreationModel accountCreationModel, boolean saveProfile);
-
-    void setProfile(AccountCreationModel model);
-}
diff --git a/ring-android/app/src/main/java/cx/ring/fragments/SecurityAccountView.java b/ring-android/libringclient/src/main/java/net/jami/account/ProfileCreationView.kt
similarity index 68%
rename from ring-android/app/src/main/java/cx/ring/fragments/SecurityAccountView.java
rename to ring-android/libringclient/src/main/java/net/jami/account/ProfileCreationView.kt
index 428cb9823..e8067193a 100644
--- a/ring-android/app/src/main/java/cx/ring/fragments/SecurityAccountView.java
+++ b/ring-android/libringclient/src/main/java/net/jami/account/ProfileCreationView.kt
@@ -17,19 +17,16 @@
  *  along with this program; if not, write to the Free Software
  *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
-package cx.ring.fragments;
+package net.jami.account
 
-import java.util.ArrayList;
+import net.jami.model.AccountCreationModel
 
-import net.jami.model.AccountConfig;
-import net.jami.model.AccountCredentials;
-
-
-public interface SecurityAccountView {
-
-    void removeAllCredentials();
-
-    void addAllCredentials(ArrayList<AccountCredentials> credentials);
-
-    void setDetails(AccountConfig config, String[] tlsMethods);
-}
+interface ProfileCreationView {
+    fun displayProfileName(profileName: String)
+    fun goToGallery()
+    fun goToPhotoCapture()
+    fun askStoragePermission()
+    fun askPhotoPermission()
+    fun goToNext(accountCreationModel: AccountCreationModel, saveProfile: Boolean)
+    fun setProfile(model: AccountCreationModel)
+}
\ No newline at end of file
diff --git a/ring-android/libringclient/src/main/java/net/jami/account/SIPCreationPresenter.kt b/ring-android/libringclient/src/main/java/net/jami/account/SIPCreationPresenter.kt
new file mode 100644
index 000000000..3ba0d1213
--- /dev/null
+++ b/ring-android/libringclient/src/main/java/net/jami/account/SIPCreationPresenter.kt
@@ -0,0 +1,174 @@
+/*
+ *  Copyright (C) 2004-2021 Savoir-faire Linux Inc.
+ *
+ *  Author: Thibault Wittemberg <thibault.wittemberg@savoirfairelinux.com>
+ *  Author: Adrien Béraud <adrien.beraud@savoirfairelinux.com>
+ *
+ *  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, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+package net.jami.account
+
+import ezvcard.VCard
+import ezvcard.property.FormattedName
+import ezvcard.property.RawProperty
+import ezvcard.property.Uid
+import io.reactivex.rxjava3.core.Scheduler
+import io.reactivex.rxjava3.core.Single
+import io.reactivex.rxjava3.observers.DisposableObserver
+import net.jami.model.Account
+import net.jami.model.AccountConfig
+import net.jami.model.ConfigKey
+import net.jami.model.Profile
+import net.jami.mvp.RootPresenter
+import net.jami.services.AccountService
+import net.jami.services.DeviceRuntimeService
+import net.jami.services.VCardService
+import net.jami.utils.Log
+import net.jami.utils.VCardUtils
+import java.util.*
+import javax.inject.Inject
+import javax.inject.Named
+
+class SIPCreationPresenter @Inject constructor(
+    private val mAccountService: AccountService,
+    private val mDeviceService: DeviceRuntimeService,
+    @Named("UiScheduler")
+    private var mUiScheduler: Scheduler
+) : RootPresenter<SIPCreationView>() {
+    private var mAccount: Account? = null
+
+    /**
+     * Attempts to register the account specified by the form. If there are form errors (invalid or missing fields, etc.), the
+     * errors are presented and no actual creation attempt is made.
+     *
+     * @param hostname      hostname account value
+     * @param username      username account value
+     * @param password      password account value
+     * @param bypassWarning Report eventual warning to the user
+     */
+    fun startCreation(hostname: String?, proxy: String?, username: String?, password: String?, bypassWarning: Boolean) {
+        view?.resetErrors()
+
+        // Store values at the time of the login attempt.
+        var warningIPAccount = false
+        if (hostname != null && hostname.isEmpty()) {
+            warningIPAccount = true
+        }
+        if (!warningIPAccount && (password == null || password.trim { it <= ' ' }.isEmpty())) {
+            view?.showPasswordError()
+            return
+        }
+        if (!warningIPAccount && (username == null || username.trim { it <= ' ' }.isEmpty())) {
+            view?.showUsernameError()
+            return
+        }
+        if (warningIPAccount && !bypassWarning) {
+            view?.showIP2IPWarning()
+        } else {
+            val accountDetails = initAccountDetails()
+            if (username != null)
+                accountDetails[ConfigKey.ACCOUNT_ALIAS.key()] = username
+            if (hostname != null && hostname.isNotEmpty()) {
+                accountDetails[ConfigKey.ACCOUNT_HOSTNAME.key()] = hostname
+                if (proxy != null)
+                    accountDetails[ConfigKey.ACCOUNT_ROUTESET.key()] = proxy
+                if (username != null)
+                    accountDetails[ConfigKey.ACCOUNT_USERNAME.key()] = username
+                if (password != null)
+                    accountDetails[ConfigKey.ACCOUNT_PASSWORD.key()] = password
+            }
+            registerAccount(accountDetails)
+        }
+    }
+
+    fun removeAccount() {
+        mAccount?.let { account ->
+            mAccountService.removeAccount(account.accountID)
+            mAccount = null
+        }
+    }
+
+    private fun registerAccount(accountDetails: Map<String, String>) {
+        view?.showLoading()
+        mCompositeDisposable.add(
+            mAccountService.addAccount(accountDetails)
+                .observeOn(mUiScheduler)
+                .subscribeWith(object : DisposableObserver<Account>() {
+                    override fun onNext(account: Account) {
+                        mAccount = account
+                        when (account.registrationState) {
+                            AccountConfig.STATE_REGISTERED, AccountConfig.STATE_SUCCESS, AccountConfig.STATE_READY -> {
+                                saveProfile(account.accountID)
+                                view?.showRegistrationSuccess()
+                                dispose()
+                            }
+                            AccountConfig.STATE_ERROR_NETWORK -> {
+                                view?.showRegistrationNetworkError()
+                                dispose()
+                            }
+                            AccountConfig.STATE_TRYING, AccountConfig.STATE_UNREGISTERED -> return
+                            else -> {
+                                view?.showRegistrationError()
+                                dispose()
+                            }
+                        }
+                    }
+
+                    override fun onError(e: Throwable) {
+                        view?.showRegistrationError()
+                        dispose()
+                    }
+
+                    override fun onComplete() {
+                        dispose()
+                    }
+                })
+        )
+    }
+
+    private fun initAccountDetails(): MutableMap<String, String> {
+        val accountDetails: MutableMap<String, String> =
+            mAccountService.getAccountTemplate(AccountConfig.ACCOUNT_TYPE_SIP).blockingGet()
+        for ((key, value) in accountDetails) {
+            Log.d(TAG, "Default account detail: $key -> $value")
+        }
+        accountDetails[ConfigKey.VIDEO_ENABLED.key()] = true.toString()
+
+        //~ Sipinfo is forced for any sipaccount since overrtp is not supported yet.
+        //~ This will have to be removed when it will be supported.
+        accountDetails[ConfigKey.ACCOUNT_DTMF_TYPE.key()] = "sipinfo"
+        return accountDetails
+    }
+
+    private fun saveProfile(accountID: String) {
+        val account = mAccount ?: return
+        val vcard = VCard()
+        var formattedName = account.username
+        if (formattedName == null || formattedName.isEmpty()) {
+            formattedName = account.alias
+        }
+        vcard.formattedName = FormattedName(formattedName)
+        val vcardUid = formattedName + accountID
+        vcard.uid = Uid(vcardUid)
+        vcard.removeProperties(RawProperty::class.java)
+        VCardUtils.saveLocalProfileToDisk(vcard, accountID, mDeviceService.provideFilesDir())
+            .subscribe()
+        account.loadedProfile = Single.just(Profile(formattedName, null))
+    }
+
+    companion object {
+        private val TAG = SIPCreationPresenter::class.simpleName!!
+    }
+}
\ No newline at end of file
diff --git a/ring-android/libringclient/src/main/java/net/jami/mvp/SIPCreationView.java b/ring-android/libringclient/src/main/java/net/jami/account/SIPCreationView.kt
similarity index 72%
rename from ring-android/libringclient/src/main/java/net/jami/mvp/SIPCreationView.java
rename to ring-android/libringclient/src/main/java/net/jami/account/SIPCreationView.kt
index 06dc43fdd..d5a560cae 100644
--- a/ring-android/libringclient/src/main/java/net/jami/mvp/SIPCreationView.java
+++ b/ring-android/libringclient/src/main/java/net/jami/account/SIPCreationView.kt
@@ -17,23 +17,15 @@
  *  along with this program; if not, write to the Free Software
  *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
-package net.jami.mvp;
-
-public interface SIPCreationView {
-
-    void showUsernameError();
-
-    void showLoading();
-
-    void resetErrors();
-
-    void showPasswordError();
-
-    void showIP2IPWarning();
-
-    void showRegistrationNetworkError();
-
-    void showRegistrationError();
-
-    void showRegistrationSuccess();
-}
+package net.jami.account
+
+interface SIPCreationView {
+    fun showUsernameError()
+    fun showLoading()
+    fun resetErrors()
+    fun showPasswordError()
+    fun showIP2IPWarning()
+    fun showRegistrationNetworkError()
+    fun showRegistrationError()
+    fun showRegistrationSuccess()
+}
\ No newline at end of file
diff --git a/ring-android/libringclient/src/main/java/net/jami/account/SecurityAccountPresenter.kt b/ring-android/libringclient/src/main/java/net/jami/account/SecurityAccountPresenter.kt
new file mode 100644
index 000000000..8045aff30
--- /dev/null
+++ b/ring-android/libringclient/src/main/java/net/jami/account/SecurityAccountPresenter.kt
@@ -0,0 +1,82 @@
+/*
+ *  Copyright (C) 2004-2021 Savoir-faire Linux Inc.
+ *
+ *  Author: Hadrien De Sousa <hadrien.desousa@savoirfairelinux.com>
+ *
+ *  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, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+package net.jami.account
+
+import net.jami.model.Account
+import net.jami.model.AccountCredentials
+import net.jami.model.ConfigKey
+import net.jami.mvp.RootPresenter
+import net.jami.services.AccountService
+import javax.inject.Inject
+
+class SecurityAccountPresenter @Inject constructor(private val mAccountService: AccountService) :
+    RootPresenter<SecurityAccountView>() {
+
+    private var mAccount: Account? = null
+
+    fun init(accountId: String) {
+        val account = mAccountService.getAccount(accountId)
+        mAccount = account
+        if (account != null) {
+            val view = view ?: return
+            view.removeAllCredentials()
+            view.addAllCredentials(account.credentials)
+            val methods = mAccountService.tlsSupportedMethods
+            val tlsMethods = methods.toTypedArray()
+            view.setDetails(account.config, tlsMethods)
+        }
+    }
+
+    fun credentialEdited(old: AccountCredentials, newCreds: AccountCredentials?) {
+        mAccount!!.removeCredential(old)
+        if (newCreds != null) {
+            // There is a new value for this credentials it means it has been edited (otherwise deleted)
+            mAccount!!.addCredential(newCreds)
+        }
+        mAccountService.setCredentials(mAccount!!.accountID, mAccount!!.credentialsHashMapList)
+        mAccountService.setAccountDetails(mAccount!!.accountID, mAccount!!.details)
+        view!!.removeAllCredentials()
+        view!!.addAllCredentials(mAccount!!.credentials)
+    }
+
+    fun credentialAdded(old: AccountCredentials?, newCreds: AccountCredentials?) {
+        mAccount!!.addCredential(newCreds!!)
+        mAccountService.setCredentials(mAccount!!.accountID, mAccount!!.credentialsHashMapList)
+        mAccountService.setAccountDetails(mAccount!!.accountID, mAccount!!.details)
+        view!!.removeAllCredentials()
+        view!!.addAllCredentials(mAccount!!.credentials)
+    }
+
+    fun tlsChanged(key: ConfigKey?, newValue: Any?) {
+        if (newValue is Boolean) {
+            mAccount!!.setDetail(key!!, (newValue as Boolean?)!!)
+        } else {
+            mAccount!!.setDetail(key!!, newValue as String?)
+        }
+        mAccountService.setCredentials(mAccount!!.accountID, mAccount!!.credentialsHashMapList)
+        mAccountService.setAccountDetails(mAccount!!.accountID, mAccount!!.details)
+    }
+
+    fun fileActivityResult(key: ConfigKey?, filePath: String?) {
+        mAccount!!.setDetail(key!!, filePath)
+        mAccountService.setCredentials(mAccount!!.accountID, mAccount!!.credentialsHashMapList)
+        mAccountService.setAccountDetails(mAccount!!.accountID, mAccount!!.details)
+    }
+}
\ No newline at end of file
diff --git a/ring-android/libringclient/src/main/java/net/jami/account/SecurityAccountView.kt b/ring-android/libringclient/src/main/java/net/jami/account/SecurityAccountView.kt
new file mode 100644
index 000000000..931aebb4f
--- /dev/null
+++ b/ring-android/libringclient/src/main/java/net/jami/account/SecurityAccountView.kt
@@ -0,0 +1,29 @@
+/*
+ *  Copyright (C) 2004-2021 Savoir-faire Linux Inc.
+ *
+ *  Author: Hadrien De Sousa <hadrien.desousa@savoirfairelinux.com>
+ *
+ *  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, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+package net.jami.account
+
+import net.jami.model.AccountConfig
+import net.jami.model.AccountCredentials
+
+interface SecurityAccountView {
+    fun removeAllCredentials()
+    fun addAllCredentials(credentials: List<AccountCredentials>)
+    fun setDetails(config: AccountConfig, tlsMethods: Array<String>)
+}
diff --git a/ring-android/libringclient/src/main/java/net/jami/call/CallPresenter.kt b/ring-android/libringclient/src/main/java/net/jami/call/CallPresenter.kt
index afe93d983..6ae02f6b2 100644
--- a/ring-android/libringclient/src/main/java/net/jami/call/CallPresenter.kt
+++ b/ring-android/libringclient/src/main/java/net/jami/call/CallPresenter.kt
@@ -51,7 +51,7 @@ class CallPresenter @Inject constructor(
     private val mDeviceRuntimeService: DeviceRuntimeService,
     private val mConversationFacade: ConversationFacade,
     @param:Named("UiScheduler") private val mUiScheduler: Scheduler
-    ) : RootPresenter<CallView>() {
+) : RootPresenter<CallView>() {
     private var mConference: Conference? = null
     private val mPendingCalls: MutableList<Call> = ArrayList()
     private val mPendingSubject: Subject<List<Call>> = BehaviorSubject.createDefault(mPendingCalls)
@@ -101,12 +101,13 @@ class CallPresenter @Inject constructor(
                     if (mSipCall != null && mSipCall.getContact() != null) {
                         getView().updateContactBubble(mSipCall.getContact());
                     }
-                }));*/mCompositeDisposable.add(mHardwareService.getVideoEvents()
+                }));*/
+        mCompositeDisposable.add(mHardwareService.getVideoEvents()
             .observeOn(mUiScheduler)
             .subscribe { event: VideoEvent -> onVideoEvent(event) })
         mCompositeDisposable.add(mHardwareService.audioState
             .observeOn(mUiScheduler)
-            .subscribe { state: AudioState? -> getView()!!.updateAudioState(state) })
+            .subscribe { state: AudioState -> this.view?.updateAudioState(state) })
 
         /*mCompositeDisposable.add(mHardwareService
                 .getBluetoothEvents()
@@ -141,7 +142,7 @@ class CallPresenter @Inject constructor(
                 confUpdate(conference)
             }) { e: Throwable ->
                 hangupCall()
-                Log.e(TAG, "Error with initOutgoing: " + e.message)
+                Log.e(TAG, "Error with initOutgoing: " + e.message, e)
             })
         showConference(callObservable)
     }
@@ -171,7 +172,7 @@ class CallPresenter @Inject constructor(
                     callInitialized = true
                     view!!.prepareCall(true)
                 }
-            }) { e: Throwable? ->
+            }) { e: Throwable ->
                 hangupCall()
                 Log.e(TAG, "Error with initIncoming, preparing call flow :", e)
             })
@@ -183,7 +184,7 @@ class CallPresenter @Inject constructor(
                     contactUpdate(call)
                     confUpdate(call)
                 }
-            }) { e: Throwable? ->
+            }) { e: Throwable ->
                 hangupCall()
                 Log.e(TAG, "Error with initIncoming, action view flow: ", e)
             })
@@ -192,20 +193,17 @@ class CallPresenter @Inject constructor(
 
     private fun showConference(conference: Observable<Conference>) {
         var conference = conference
-        conference = conference
-            .distinctUntilChanged()
+        conference = conference.distinctUntilChanged()
         mCompositeDisposable.add(conference
             .switchMap { obj: Conference -> obj.participantInfo }
             .observeOn(mUiScheduler)
-            .subscribe(
-                { info: List<ParticipantInfo> -> view?.updateConfInfo(info) }
-            ) { e: Throwable -> Log.e(TAG, "Error with initIncoming, action view flow: ", e) })
+            .subscribe({ info: List<ParticipantInfo> -> view?.updateConfInfo(info) })
+            { e: Throwable -> Log.e(TAG, "Error with initIncoming, action view flow: ", e) })
         mCompositeDisposable.add(conference
             .switchMap { obj: Conference -> obj.participantRecording }
             .observeOn(mUiScheduler)
-            .subscribe(
-                { contacts: Set<Contact> -> view?.updateParticipantRecording(contacts) }
-            ) { e: Throwable -> Log.e(TAG, "Error with initIncoming, action view flow: ", e) })
+            .subscribe({ contacts: Set<Contact> -> view?.updateParticipantRecording(contacts) })
+            { e: Throwable -> Log.e(TAG, "Error with initIncoming, action view flow: ", e) })
     }
 
     fun prepareOptionMenu() {
@@ -216,7 +214,7 @@ class CallPresenter @Inject constructor(
         val displayPluginsButton = view!!.displayPluginsButton()
         val showPluginBtn = displayPluginsButton && mOnGoingCall && mConference != null
         val hasMultipleCamera = mHardwareService.cameraCount > 1 && mOnGoingCall && !isAudioOnly
-        view!!.initMenu(isSpeakerOn, hasMultipleCamera, canDial, showPluginBtn, mOnGoingCall)
+        view?.initMenu(isSpeakerOn, hasMultipleCamera, canDial, showPluginBtn, mOnGoingCall)
     }
 
     fun chatClick() {
@@ -226,10 +224,9 @@ class CallPresenter @Inject constructor(
         val firstCall = mConference!!.participants[0] ?: return
         val c = firstCall.conversation
         if (c is Conversation) {
-            val conversation = c
-            view!!.goToConversation(conversation.accountId, conversation.uri)
+            view?.goToConversation(c.accountId, c.uri)
         } else if (firstCall.contact != null) {
-            view!!.goToConversation(firstCall.account, firstCall.contact!!.conversationUri.blockingFirst())
+            view?.goToConversation(firstCall.account!!, firstCall.contact!!.conversationUri.blockingFirst())
         }
     }
 
@@ -248,9 +245,9 @@ class CallPresenter @Inject constructor(
         get() = mCallService.isCaptureMuted
 
     fun switchVideoInputClick() {
-        if (mConference == null) return
-        mHardwareService.switchInput(mConference!!.id, false)
-        view!!.switchCameraIcon(mHardwareService.isPreviewFromFrontCamera)
+        val conference = mConference ?: return
+        mHardwareService.switchInput(conference.id, false)
+        view?.switchCameraIcon(mHardwareService.isPreviewFromFrontCamera)
     }
 
     fun configurationChanged(rotation: Int) {
@@ -283,61 +280,69 @@ class CallPresenter @Inject constructor(
         finish()
     }
 
-    fun videoSurfaceCreated(holder: Any?) {
+    fun videoSurfaceCreated(holder: Any) {
         if (mConference == null) {
             return
         }
         val newId = mConference!!.id
         if (newId != currentSurfaceId) {
-            mHardwareService.removeVideoSurface(currentSurfaceId)
+            currentSurfaceId?.let { id ->
+                mHardwareService.removeVideoSurface(id)
+            }
             currentSurfaceId = newId
         }
         mHardwareService.addVideoSurface(mConference!!.id, holder)
-        view!!.displayContactBubble(false)
+        view?.displayContactBubble(false)
     }
 
-    fun videoSurfaceUpdateId(newId: String?) {
+    private fun videoSurfaceUpdateId(newId: String) {
         if (newId != currentSurfaceId) {
-            mHardwareService.updateVideoSurfaceId(currentSurfaceId, newId)
+            currentSurfaceId?.let { oldId ->
+                mHardwareService.updateVideoSurfaceId(oldId, newId)
+            }
             currentSurfaceId = newId
         }
     }
 
-    fun pluginSurfaceCreated(holder: Any?) {
+    fun pluginSurfaceCreated(holder: Any) {
         if (mConference == null) {
             return
         }
         val newId = mConference!!.pluginId
         if (newId != currentPluginSurfaceId) {
-            mHardwareService.removeVideoSurface(currentPluginSurfaceId)
+            currentPluginSurfaceId?.let { id ->
+                mHardwareService.removeVideoSurface(id)
+            }
             currentPluginSurfaceId = newId
         }
         mHardwareService.addVideoSurface(mConference!!.pluginId, holder)
-        view!!.displayContactBubble(false)
+        view?.displayContactBubble(false)
     }
 
-    fun pluginSurfaceUpdateId(newId: String?) {
+    private fun pluginSurfaceUpdateId(newId: String) {
         if (newId != currentPluginSurfaceId) {
-            mHardwareService.updateVideoSurfaceId(currentPluginSurfaceId, newId)
+            currentPluginSurfaceId?.let { oldId ->
+                mHardwareService.updateVideoSurfaceId(oldId, newId)
+            }
             currentPluginSurfaceId = newId
         }
     }
 
-    fun previewVideoSurfaceCreated(holder: Any?) {
+    fun previewVideoSurfaceCreated(holder: Any) {
         mHardwareService.addPreviewVideoSurface(holder, mConference)
         //mHardwareService.startCapture(null);
     }
 
     fun videoSurfaceDestroyed() {
-        if (currentSurfaceId != null) {
-            mHardwareService.removeVideoSurface(currentSurfaceId)
+        currentSurfaceId?.let { id ->
+            mHardwareService.removeVideoSurface(id)
             currentSurfaceId = null
         }
     }
 
     fun pluginSurfaceDestroyed() {
-        if (currentPluginSurfaceId != null) {
-            mHardwareService.removeVideoSurface(currentPluginSurfaceId)
+        currentPluginSurfaceId?.let { id ->
+            mHardwareService.removeVideoSurface(id)
             currentPluginSurfaceId = null
         }
     }
@@ -357,13 +362,13 @@ class CallPresenter @Inject constructor(
 
     fun uiVisibilityChanged(displayed: Boolean) {
         Log.w(TAG, "uiVisibilityChanged $mOnGoingCall $displayed")
-        val view = view
         view?.displayHangupButton(mOnGoingCall && displayed)
     }
 
     private fun finish() {
-        if (timeUpdateTask != null && !timeUpdateTask!!.isDisposed) {
-            timeUpdateTask!!.dispose()
+        timeUpdateTask?.let { task ->
+            if (!task.isDisposed)
+                task.dispose()
             timeUpdateTask = null
         }
         mConference = null
@@ -375,9 +380,7 @@ class CallPresenter @Inject constructor(
     private fun contactUpdate(conference: Conference) {
         if (mConference !== conference) {
             mConference = conference
-            if (contactDisposable != null && !contactDisposable!!.isDisposed) {
-                contactDisposable!!.dispose()
-            }
+            contactDisposable?.apply { dispose() }
             if (conference.participants.isEmpty()) return
 
             // Updates of participant (and  pending participant) list
@@ -392,36 +395,27 @@ class CallPresenter @Inject constructor(
                 }
 
             // Updates of individual contacts
-            val contactsObservable = callsObservable
-                .flatMapSingle { calls: List<Call> ->
-                    Observable.fromIterable(calls)
-                        .map { call: Call -> mContactService.observeContact(call.account!!, call.contact!!, false)
-                                .map { call } }
-                        .toList(calls.size)
-                }
+            val contactsObservable = callsObservable.flatMapSingle { calls: List<Call> ->
+                Observable.fromIterable(calls)
+                    .map { call: Call -> mContactService.observeContact(call.account!!, call.contact!!, false)
+                            .map { call } }
+                    .toList(calls.size)
+            }
 
             // Combined updates of contacts as participant list updates
             val contactUpdates = contactsObservable
-                .switchMap { list: List<Observable<Call>>? ->
-                    Observable
-                        .combineLatest(list) { objects: Array<Any> ->
-                            Log.w(TAG, "flatMapObservable " + objects.size)
-                            val calls = ArrayList<Call>(objects.size)
-                            for (call in objects) calls.add(call as Call)
-                            calls
-                        }
-                }
-                .filter { list: List<Call> -> !list.isEmpty() }
+                .switchMap { list: List<Observable<Call>> -> Observable.combineLatest(list) { objects: Array<Any> ->
+                    Log.w(TAG, "flatMapObservable " + objects.size)
+                    val calls = ArrayList<Call>(objects.size)
+                    for (call in objects) calls.add(call as Call)
+                    calls
+                } }
+                .filter { list: List<Call> -> list.isNotEmpty() }
             contactDisposable = contactUpdates
                 .observeOn(mUiScheduler)
-                .subscribe({ cs: List<Call>? -> view!!.updateContactBubble(cs) }) { e: Throwable? ->
-                    Log.e(
-                        TAG,
-                        "Error updating contact data",
-                        e
-                    )
-                }
-            mCompositeDisposable.add(contactDisposable)
+                .subscribe({ cs: List<Call> -> view?.updateContactBubble(cs) })
+                { e: Throwable -> Log.e(TAG, "Error updating contact data", e) }
+                .apply { mCompositeDisposable.add(this) }
         }
         mPendingSubject.onNext(mPendingCalls)
     }
@@ -450,7 +444,7 @@ class CallPresenter @Inject constructor(
                     permissionChanged = false
                 }
             }
-            if (timeUpdateTask != null) timeUpdateTask!!.dispose()
+            timeUpdateTask?.dispose()
             timeUpdateTask = mUiScheduler.schedulePeriodicallyDirect({ updateTime() }, 0, 1, TimeUnit.SECONDS)
         } else if (call.isRinging) {
             val scall = call.call!!
@@ -500,30 +494,28 @@ class CallPresenter @Inject constructor(
 
     private fun onVideoEvent(event: VideoEvent) {
         Log.d(TAG, "VIDEO_EVENT: " + event.start + " " + event.callId + " " + event.w + "x" + event.h)
+        val view = view ?: return
         if (event.start) {
-            view!!.displayVideoSurface(true, !isPipMode && mDeviceRuntimeService.hasVideoPermission())
+            view.displayVideoSurface(true, !isPipMode && mDeviceRuntimeService.hasVideoPermission())
         } else if (mConference != null && mConference!!.id == event.callId) {
-            view!!.displayVideoSurface(
-                event.started,
-                event.started && !isPipMode && mDeviceRuntimeService.hasVideoPermission()
-            )
+            view.displayVideoSurface(event.started, event.started && !isPipMode && mDeviceRuntimeService.hasVideoPermission())
             if (event.started) {
                 videoWidth = event.w
                 videoHeight = event.h
-                view!!.resetVideoSize(videoWidth, videoHeight)
+                view.resetVideoSize(videoWidth, videoHeight)
             }
         } else if (event.callId == null) {
             if (event.started) {
                 previewWidth = event.w
                 previewHeight = event.h
-                view!!.resetPreviewVideoSize(previewWidth, previewHeight, event.rot)
+                view.resetPreviewVideoSize(previewWidth, previewHeight, event.rot)
             }
         }
         if (mConference != null && mConference!!.pluginId == event.callId) {
             if (event.started) {
                 previewWidth = event.w
                 previewHeight = event.h
-                view!!.resetPluginPreviewVideoSize(previewWidth, previewHeight, event.rot)
+                view.resetPluginPreviewVideoSize(previewWidth, previewHeight, event.rot)
             }
         }
         /*if (event.started || event.start) {
@@ -571,9 +563,10 @@ class CallPresenter @Inject constructor(
         }
     }
 
-    fun toggleCallMediaHandler(id: String?, toggle: Boolean) {
-        if (mConference != null && mConference!!.isOnGoing && mConference!!.hasVideo()) {
-            view!!.toggleCallMediaHandler(id, mConference!!.id, toggle)
+    fun toggleCallMediaHandler(id: String, toggle: Boolean) {
+        val conference = mConference ?: return
+        if (conference.isOnGoing && conference.hasVideo()) {
+            view?.toggleCallMediaHandler(id, conference.id, toggle)
         }
     }
 
@@ -659,8 +652,8 @@ class CallPresenter @Inject constructor(
     }
 
     fun openParticipantContact(info: ParticipantInfo) {
-        val call = info.call ?: mConference!!.firstCall!!
-        view!!.goToContact(call.account, info.contact)
+        val call = info.call ?: mConference?.firstCall ?: return
+        view?.goToContact(call.account!!, info.contact)
     }
 
     fun stopCapture() {
diff --git a/ring-android/libringclient/src/main/java/net/jami/call/CallView.java b/ring-android/libringclient/src/main/java/net/jami/call/CallView.java
deleted file mode 100644
index f82a97544..000000000
--- a/ring-android/libringclient/src/main/java/net/jami/call/CallView.java
+++ /dev/null
@@ -1,91 +0,0 @@
-/*
- *  Copyright (C) 2004-2021 Savoir-faire Linux Inc.
- *
- *  Author: Hadrien De Sousa <hadrien.desousa@savoirfairelinux.com>
- *
- *  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, write to the Free Software
- *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-package net.jami.call;
-
-import java.util.List;
-import java.util.Set;
-
-import net.jami.model.Call;
-import net.jami.model.Contact;
-import net.jami.model.Conference;
-import net.jami.model.Uri;
-import net.jami.services.HardwareService;
-
-public interface CallView {
-
-    void displayContactBubble(boolean display);
-
-    void displayVideoSurface(boolean displayVideoSurface, boolean displayPreviewContainer);
-
-    void displayPreviewSurface(boolean display);
-
-    void displayHangupButton(boolean display);
-
-    void displayDialPadKeyboard();
-
-    void switchCameraIcon(boolean isFront);
-    void updateAudioState(HardwareService.AudioState state);
-
-    void updateMenu();
-
-    void updateTime(long duration);
-
-    void updateContactBubble(List<Call> contact);
-
-    void updateCallStatus(Call.CallStatus callState);
-
-    void initMenu(boolean isSpeakerOn, boolean displayFlip, boolean canDial, boolean showPluginBtn, boolean onGoingCall);
-
-    void initNormalStateDisplay(boolean audioOnly, boolean muted);
-
-    void initIncomingCallDisplay();
-
-    void initOutGoingCallDisplay();
-
-    void resetPreviewVideoSize(int previewWidth, int previewHeight, int rot);
-    void resetPluginPreviewVideoSize(int previewWidth, int previewHeight, int rot);
-    void resetVideoSize(int videoWidth, int videoHeight);
-
-    void goToConversation(String accountId, Uri conversationId);
-
-    void goToAddContact(Contact contact);
-
-    void startAddParticipant(String conferenceId);
-
-    void finish();
-
-    void onUserLeave();
-
-    void enterPipMode(String callId);
-
-    void prepareCall(boolean isIncoming);
-
-    void handleCallWakelock(boolean isAudioOnly);
-
-    void goToContact(String accountId, Contact contact);
-
-    boolean displayPluginsButton();
-
-    void updateConfInfo(List<Conference.ParticipantInfo> info);
-
-    void updateParticipantRecording(Set<Contact> contacts);
-
-    void toggleCallMediaHandler(String id, String callId, boolean toggle);
-}
diff --git a/ring-android/libringclient/src/main/java/net/jami/call/CallView.kt b/ring-android/libringclient/src/main/java/net/jami/call/CallView.kt
new file mode 100644
index 000000000..62843bbe3
--- /dev/null
+++ b/ring-android/libringclient/src/main/java/net/jami/call/CallView.kt
@@ -0,0 +1,61 @@
+/*
+ *  Copyright (C) 2004-2021 Savoir-faire Linux Inc.
+ *
+ *  Author: Hadrien De Sousa <hadrien.desousa@savoirfairelinux.com>
+ *
+ *  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, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+package net.jami.call
+
+import net.jami.model.Call
+import net.jami.model.Call.CallStatus
+import net.jami.model.Conference.ParticipantInfo
+import net.jami.model.Contact
+import net.jami.model.Uri
+import net.jami.services.HardwareService.AudioState
+
+interface CallView {
+    fun displayContactBubble(display: Boolean)
+    fun displayVideoSurface(displayVideoSurface: Boolean, displayPreviewContainer: Boolean)
+    fun displayPreviewSurface(display: Boolean)
+    fun displayHangupButton(display: Boolean)
+    fun displayDialPadKeyboard()
+    fun switchCameraIcon(isFront: Boolean)
+    fun updateAudioState(state: AudioState)
+    fun updateMenu()
+    fun updateTime(duration: Long)
+    fun updateContactBubble(contact: List<Call>)
+    fun updateCallStatus(callState: CallStatus)
+    fun initMenu(isSpeakerOn: Boolean, displayFlip: Boolean, canDial: Boolean, showPluginBtn: Boolean, onGoingCall: Boolean)
+    fun initNormalStateDisplay(audioOnly: Boolean, muted: Boolean)
+    fun initIncomingCallDisplay()
+    fun initOutGoingCallDisplay()
+    fun resetPreviewVideoSize(previewWidth: Int, previewHeight: Int, rot: Int)
+    fun resetPluginPreviewVideoSize(previewWidth: Int, previewHeight: Int, rot: Int)
+    fun resetVideoSize(videoWidth: Int, videoHeight: Int)
+    fun goToConversation(accountId: String, conversationId: Uri)
+    fun goToAddContact(contact: Contact)
+    fun startAddParticipant(conferenceId: String)
+    fun finish()
+    fun onUserLeave()
+    fun enterPipMode(callId: String)
+    fun prepareCall(isIncoming: Boolean)
+    fun handleCallWakelock(isAudioOnly: Boolean)
+    fun goToContact(accountId: String, contact: Contact)
+    fun displayPluginsButton(): Boolean
+    fun updateConfInfo(info: List<ParticipantInfo>)
+    fun updateParticipantRecording(contacts: Set<Contact>)
+    fun toggleCallMediaHandler(id: String, callId: String, toggle: Boolean)
+}
\ No newline at end of file
diff --git a/ring-android/libringclient/src/main/java/net/jami/contactrequests/BlockListPresenter.kt b/ring-android/libringclient/src/main/java/net/jami/contactrequests/BlockListPresenter.kt
index 6a8b4eb6b..7509b9d7e 100644
--- a/ring-android/libringclient/src/main/java/net/jami/contactrequests/BlockListPresenter.kt
+++ b/ring-android/libringclient/src/main/java/net/jami/contactrequests/BlockListPresenter.kt
@@ -35,23 +35,14 @@ class BlockListPresenter @Inject constructor(
 ) : RootPresenter<BlockListView>() {
     private var mAccountID: String? = null
 
-    override fun bindView(view: BlockListView) {
-        super.bindView(view)
-        /*if (mAccountID != null) {
-            setAccountId(mAccountID)
-        }*/
-    }
-
     private fun updateList(list: Collection<Contact>) {
-        if (view == null) {
-            return
-        }
+        val view = view ?: return
         if (list.isEmpty()) {
-            view!!.hideListView()
-            view!!.displayEmptyListMessage(true)
+            view.hideListView()
+            view.displayEmptyListMessage(true)
         } else {
-            view!!.updateView(list)
-            view!!.displayEmptyListMessage(false)
+            view.updateView(list)
+            view.displayEmptyListMessage(false)
         }
     }
 
@@ -64,13 +55,8 @@ class BlockListPresenter @Inject constructor(
             .getAccountSingle(accountID)
             .flatMapObservable(Account::bannedContactsUpdates)
             .observeOn(mUiScheduler)
-            .subscribe({ list: Collection<Contact> -> updateList(list) }) { e: Throwable ->
-                Log.e(
-                    TAG,
-                    "Error showing blacklist",
-                    e
-                )
-            })
+            .subscribe({ list: Collection<Contact> -> updateList(list) })
+            { e: Throwable -> Log.e(TAG, "Error showing blacklist", e) })
         mAccountID = accountID
     }
 
diff --git a/ring-android/libringclient/src/main/java/net/jami/contactrequests/ContactRequestsPresenter.kt b/ring-android/libringclient/src/main/java/net/jami/contactrequests/ContactRequestsPresenter.kt
index 9bc2d5137..2ce7cd965 100644
--- a/ring-android/libringclient/src/main/java/net/jami/contactrequests/ContactRequestsPresenter.kt
+++ b/ring-android/libringclient/src/main/java/net/jami/contactrequests/ContactRequestsPresenter.kt
@@ -48,7 +48,7 @@ class ContactRequestsPresenter @Inject internal constructor(
                     { ob -> ob as SmartListViewModel } }
             }
             .observeOn(mUiScheduler)
-            .subscribe({ viewModels -> getView()?.updateView(viewModels, mCompositeDisposable) })
+            .subscribe({ viewModels -> view?.updateView(viewModels, mCompositeDisposable) })
             { e: Throwable -> Log.d(TAG, "updateList subscribe onError", e) })
     }
 
@@ -67,7 +67,7 @@ class ContactRequestsPresenter @Inject internal constructor(
         }
     }
 
-    fun contactRequestClicked(accountId: String?, uri: Uri?) {
+    fun contactRequestClicked(accountId: String, uri: Uri) {
         view?.goToConversation(accountId, uri)
     }
 
diff --git a/ring-android/libringclient/src/main/java/net/jami/contactrequests/ContactRequestsView.java b/ring-android/libringclient/src/main/java/net/jami/contactrequests/ContactRequestsView.kt
similarity index 64%
rename from ring-android/libringclient/src/main/java/net/jami/contactrequests/ContactRequestsView.java
rename to ring-android/libringclient/src/main/java/net/jami/contactrequests/ContactRequestsView.kt
index ca32ddac2..3b1ad42df 100644
--- a/ring-android/libringclient/src/main/java/net/jami/contactrequests/ContactRequestsView.java
+++ b/ring-android/libringclient/src/main/java/net/jami/contactrequests/ContactRequestsView.kt
@@ -16,20 +16,14 @@
  *  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 net.jami.contactrequests
 
-package net.jami.contactrequests;
+import io.reactivex.rxjava3.disposables.CompositeDisposable
+import net.jami.model.Uri
+import net.jami.smartlist.SmartListViewModel
 
-import java.util.List;
-
-import net.jami.model.Uri;
-import net.jami.smartlist.SmartListViewModel;
-
-import io.reactivex.rxjava3.disposables.CompositeDisposable;
-
-public interface ContactRequestsView {
-
-    void updateView(List<SmartListViewModel> list, CompositeDisposable disposable);
-    void updateItem(SmartListViewModel item);
-
-    void goToConversation(String accountId, Uri contactId);
-}
+interface ContactRequestsView {
+    fun updateView(list: MutableList<SmartListViewModel>, disposable: CompositeDisposable)
+    fun updateItem(item: SmartListViewModel)
+    fun goToConversation(accountId: String, contactId: Uri)
+}
\ No newline at end of file
diff --git a/ring-android/libringclient/src/main/java/net/jami/conversation/ConversationPresenter.kt b/ring-android/libringclient/src/main/java/net/jami/conversation/ConversationPresenter.kt
index 1914b5b47..9c3af2350 100644
--- a/ring-android/libringclient/src/main/java/net/jami/conversation/ConversationPresenter.kt
+++ b/ring-android/libringclient/src/main/java/net/jami/conversation/ConversationPresenter.kt
@@ -34,9 +34,8 @@ import net.jami.model.Conversation.ElementStatus
 import net.jami.mvp.RootPresenter
 import net.jami.services.*
 import net.jami.utils.Log
-import net.jami.utils.StringUtils.isEmpty
-import net.jami.utils.Tuple
-import net.jami.utils.VCardUtils.vcardToString
+import net.jami.utils.StringUtils
+import net.jami.utils.VCardUtils
 import java.io.File
 import javax.inject.Inject
 import javax.inject.Named
@@ -161,8 +160,7 @@ class ConversationPresenter @Inject constructor(
                 { isConnected: Boolean, a: Account -> isConnected || a.isRegistered })
                 .observeOn(mUiScheduler)
                 .subscribe { isOk: Boolean ->
-                    val v = getView()
-                    if (v != null) {
+                    this.view?.let { v ->
                         if (!isOk) v.displayNetworkErrorPanel() else if (!account.isEnabled) {
                             v.displayAccountOfflineErrorPanel()
                         } else {
@@ -172,12 +170,12 @@ class ConversationPresenter @Inject constructor(
                 })
         disposable.add(c.sortedHistory
             .observeOn(mUiScheduler)
-            .subscribe({ conversation: List<Interaction> -> view.refreshView(conversation) }) { e: Throwable ->
+            .subscribe({ conversation: List<Interaction> -> this.view?.refreshView(conversation) }) { e: Throwable ->
                 Log.e(TAG, "Can't update element", e)
             })
         disposable.add(c.cleared
             .observeOn(mUiScheduler)
-            .subscribe({ conversation: List<Interaction> -> view.refreshView(conversation) }) { e: Throwable ->
+            .subscribe({ conversation: List<Interaction> -> this.view?.refreshView(conversation) }) { e: Throwable ->
                 Log.e(TAG, "Can't update elements", e)
             })
         disposable.add(c.contactUpdates
@@ -185,25 +183,26 @@ class ConversationPresenter @Inject constructor(
                 Observable.merge(mContactService.observeLoadedContact(c.accountId, contacts, true))
             }
             .observeOn(mUiScheduler)
-            .subscribe { contact: Contact -> getView()?.updateContact(contact) })
+            .subscribe { contact: Contact -> this.view?.updateContact(contact) })
         disposable.add(c.updatedElements
             .observeOn(mUiScheduler)
             .subscribe({ elementTuple ->
+                val v = this.view ?: return@subscribe
                 when (elementTuple.second) {
-                    ElementStatus.ADD -> view.addElement(elementTuple.first)
-                    ElementStatus.UPDATE -> view.updateElement(elementTuple.first)
-                    ElementStatus.REMOVE -> view.removeElement(elementTuple.first)
+                    ElementStatus.ADD -> v.addElement(elementTuple.first)
+                    ElementStatus.UPDATE -> v.updateElement(elementTuple.first)
+                    ElementStatus.REMOVE -> v.removeElement(elementTuple.first)
                 }
             }, { e: Throwable -> Log.e(TAG, "Can't update element", e) })
         )
         if (showTypingIndicator()) {
             disposable.add(c.composingStatus
                 .observeOn(mUiScheduler)
-                .subscribe { composingStatus: ComposingStatus -> view.setComposingStatus(composingStatus) })
+                .subscribe { composingStatus: ComposingStatus -> this.view?.setComposingStatus(composingStatus) })
         }
         disposable.add(c.getLastDisplayed()
             .observeOn(mUiScheduler)
-            .subscribe { interaction: Interaction -> view.setLastDisplayed(interaction) })
+            .subscribe { interaction: Interaction -> this.view?.setLastDisplayed(interaction) })
         disposable.add(c.calls
             .observeOn(mUiScheduler)
             .subscribe({ updateOngoingCallView(c) }) { e: Throwable ->
@@ -224,7 +223,7 @@ class ConversationPresenter @Inject constructor(
             .observeOn(mUiScheduler)
             .subscribe {
                 Log.e(TAG, "getLocationUpdates: update")
-                getView()?.showMap(c.accountId, c.uri.uri, false)
+                view?.showMap(c.accountId, c.uri.uri, false)
             }
         )
     }
@@ -239,7 +238,7 @@ class ConversationPresenter @Inject constructor(
 
     fun sendTextMessage(message: String?) {
         val conversation = mConversation
-        if (isEmpty(message) || conversation == null) {
+        if (message == null || message.isEmpty() || conversation == null) {
             return
         }
         val conference = conversation.currentCall
@@ -306,7 +305,7 @@ class ConversationPresenter @Inject constructor(
         contact.status = Contact.Status.REQUEST_SENT
         mVCardService.loadSmallVCardWithDefault(conversation.accountId, VCardService.MAX_SIZE_REQUEST)
             .subscribeOn(Schedulers.computation())
-            .subscribe({ vCard -> mAccountService.sendTrustRequest(conversation, contact.uri, Blob.fromString(vcardToString(vCard)))})
+            .subscribe({ vCard -> mAccountService.sendTrustRequest(conversation, contact.uri, Blob.fromString(VCardUtils.vcardToString(vCard)))})
             { mAccountService.sendTrustRequest(conversation, contact.uri, null) }
     }
 
@@ -405,8 +404,8 @@ class ConversationPresenter @Inject constructor(
         view?.showPluginListHandlers(mConversation!!.accountId, mConversationUri!!.uri)
     }
 
-    val path: Tuple<String, String>
-        get() = Tuple(mConversation!!.accountId, mConversationUri!!.uri)
+    val path: Pair<String, String>
+        get() = Pair(mConversation!!.accountId, mConversationUri!!.uri)
 
     fun onComposingChanged(hasMessage: Boolean) {
         if (showTypingIndicator()) {
diff --git a/ring-android/libringclient/src/main/java/net/jami/conversation/ConversationView.kt b/ring-android/libringclient/src/main/java/net/jami/conversation/ConversationView.kt
index d69902d1e..5d6b2ffbb 100644
--- a/ring-android/libringclient/src/main/java/net/jami/conversation/ConversationView.kt
+++ b/ring-android/libringclient/src/main/java/net/jami/conversation/ConversationView.kt
@@ -21,17 +21,16 @@ package net.jami.conversation
 
 import net.jami.model.*
 import net.jami.model.Account.ComposingStatus
-import net.jami.mvp.BaseView
 import java.io.File
 
-interface ConversationView : BaseView {
+interface ConversationView {
     fun refreshView(conversation: List<Interaction>)
     fun scrollToEnd()
     fun updateContact(contact: Contact)
     fun displayContact(conversation: Conversation)
     fun displayOnGoingCallPane(display: Boolean)
     fun displayNumberSpinner(conversation: Conversation, number: Uri)
-    override fun displayErrorToast(error: Error)
+    fun displayErrorToast(error: Error)
     fun hideNumberSpinner()
     fun clearMsgEdit()
     fun goToHome()
diff --git a/ring-android/libringclient/src/main/java/net/jami/model/Account.kt b/ring-android/libringclient/src/main/java/net/jami/model/Account.kt
index 5b32d3296..05e2d20ad 100644
--- a/ring-android/libringclient/src/main/java/net/jami/model/Account.kt
+++ b/ring-android/libringclient/src/main/java/net/jami/model/Account.kt
@@ -30,17 +30,15 @@ import net.jami.services.AccountService
 import net.jami.smartlist.SmartListViewModel
 import net.jami.utils.Log
 import net.jami.utils.StringUtils
-import net.jami.utils.Tuple
 import java.lang.IllegalStateException
 import java.util.*
 
 class Account(
-    bAccountID: String,
+    val accountID: String,
     details: Map<String, String>,
     credentials: List<Map<String, String>>,
     volDetails: Map<String, String>
 ) {
-    val accountID: String = bAccountID
     private var mVolatileDetails: AccountConfig
     var config: AccountConfig
         private set
@@ -75,14 +73,15 @@ class Account(
     private val mLocationStartedSubject: Subject<ContactLocationEntry> = PublishSubject.create()
 
     var historyLoader: Single<Account>? = null
-    var loadedProfile: Single<Tuple<String?, Any?>>? = null
+    var loadedProfile: Single<Profile>? = null
         set(profile) {
             field = profile
-            mProfileSubject.onNext(profile)
+            if  (profile != null)
+                mProfileSubject.onNext(profile)
         }
 
-    private val mProfileSubject: Subject<Single<Tuple<String?, Any?>>> = BehaviorSubject.create()
-    val loadedProfileObservable: Observable<Tuple<String?, Any?>> = mProfileSubject.switchMapSingle { single -> single }
+    private val mProfileSubject: Subject<Single<Profile>> = BehaviorSubject.create()
+    val loadedProfileObservable: Observable<Profile> = mProfileSubject.switchMapSingle { single -> single }
 
     fun cleanup() {
         conversationSubject.onComplete()
@@ -307,8 +306,8 @@ class Account(
         pendingChanged()
     }
 
-    fun updated(conversation: Conversation?) {
-        val key = conversation!!.uri.uri
+    fun updated(conversation: Conversation) {
+        val key = conversation.uri.uri
         synchronized(conversations) {
             if (conversation == conversations[key]) {
                 conversationUpdated(conversation)
@@ -358,7 +357,7 @@ class Account(
             conversation = getConversationByCallId(daemonId)
         }
         if (conversation == null) {
-            conversation = getByKey(txt.conversation!!.participant)
+            conversation = getByKey(txt.conversation!!.participant!!)
             txt.contact = conversation.contact
         }
         conversation.addTextMessage(txt)
@@ -421,7 +420,7 @@ class Account(
         username = config[ConfigKey.ACCOUNT_USERNAME]
     }
 
-    fun setDetail(key: ConfigKey, value: String) {
+    fun setDetail(key: ConfigKey, value: String?) {
         config.put(key, value)
     }
 
@@ -544,9 +543,7 @@ class Account(
 
     val credentialsHashMapList: List<Map<String, String>>
         get() {
-            val result = ArrayList<Map<String, String>>(
-                credentials.size
-            )
+            val result = ArrayList<Map<String, String>>(credentials.size)
             for (cred in credentials) {
                 result.add(cred.details)
             }
@@ -947,7 +944,9 @@ class Account(
 
     val accountAlias: Single<String>
         get() = loadedProfileObservable.firstOrError()
-            .map { p: Tuple<String?, Any?> -> if (StringUtils.isEmpty(p.first)) if (isJami) jamiAlias else alias else p.first }
+            .map { p -> if (p.displayName == null || p.displayName.isEmpty())
+                if (isJami) jamiAlias else alias!!
+            else p.displayName }
 
     /**
      * Registered name, fallback to Alias
@@ -958,10 +957,6 @@ class Account(
             return if (StringUtils.isEmpty(registeredName)) alias!! else registeredName
         }
 
-    fun resetProfile() {
-        loadedProfile = null
-    }
-
     fun getDataTransfer(id: String): DataTransfer? {
         return mDataTransfers[id]
     }
diff --git a/ring-android/libringclient/src/main/java/net/jami/model/AccountConfig.kt b/ring-android/libringclient/src/main/java/net/jami/model/AccountConfig.kt
index 1ed759a18..104af34d0 100644
--- a/ring-android/libringclient/src/main/java/net/jami/model/AccountConfig.kt
+++ b/ring-android/libringclient/src/main/java/net/jami/model/AccountConfig.kt
@@ -40,8 +40,11 @@ class AccountConfig(details: Map<String, String>) {
             return details
         }
 
-    fun put(key: ConfigKey, value: String) {
-        mValues[key] = value
+    fun put(key: ConfigKey, value: String?) {
+        if (value == null)
+            mValues.remove(key)
+        else
+            mValues[key] = value
     }
 
     fun put(key: ConfigKey, value: Boolean) {
diff --git a/ring-android/libringclient/src/main/java/net/jami/model/AccountCreationModel.kt b/ring-android/libringclient/src/main/java/net/jami/model/AccountCreationModel.kt
new file mode 100644
index 000000000..4273d3956
--- /dev/null
+++ b/ring-android/libringclient/src/main/java/net/jami/model/AccountCreationModel.kt
@@ -0,0 +1,76 @@
+/*
+ *  Copyright (C) 2004-2021 Savoir-faire Linux Inc.
+ *
+ *  Author: Hadrien De Sousa <hadrien.desousa@savoirfairelinux.com>
+ *
+ *  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, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+package net.jami.model
+
+import ezvcard.VCard
+import io.reactivex.rxjava3.core.Observable
+import io.reactivex.rxjava3.core.Single
+import io.reactivex.rxjava3.subjects.BehaviorSubject
+import io.reactivex.rxjava3.subjects.Subject
+import java.io.File
+import java.io.Serializable
+
+abstract class AccountCreationModel : Serializable {
+    var managementServer: String? = null
+    private var mFullName = ""
+    var username = ""
+    var password = ""
+    private var mPin = ""
+    var archive: File? = null
+    var isLink = false
+    var isPush = true
+
+    @Transient
+    var newAccount: Account? = null
+        set(account) {
+            field = account
+            profile.onNext(this)
+        }
+
+    @Transient
+    open var photo: Any? = null
+        set(photo) {
+            field = photo
+            profile.onNext(this)
+        }
+
+    @Transient
+    var accountObservable: Observable<Account>? = null
+
+    @Transient
+    protected val profile: Subject<AccountCreationModel> = BehaviorSubject.createDefault(this)
+
+    var fullName: String
+        get() = mFullName
+        set(fullName) {
+            mFullName = fullName
+            profile.onNext(this)
+        }
+    var pin: String
+        get() = mPin
+        set(pin) {
+            mPin = pin.toUpperCase()
+        }
+
+    abstract fun toVCard(): Single<VCard>
+
+    val profileUpdates: Observable<AccountCreationModel>
+        get() = profile
+}
\ No newline at end of file
diff --git a/ring-android/libringclient/src/main/java/net/jami/model/AccountCredentials.java b/ring-android/libringclient/src/main/java/net/jami/model/AccountCredentials.java
deleted file mode 100644
index c160d84b0..000000000
--- a/ring-android/libringclient/src/main/java/net/jami/model/AccountCredentials.java
+++ /dev/null
@@ -1,93 +0,0 @@
-/*
- * Copyright (C) 2004-2016 Savoir-faire Linux Inc.
- * <p>
- * Author: Alexandre Lision <alexandre.lision@savoirfairelinux.com>
- * Adrien Béraud <adrien.beraud@savoirfairelinux.com>
- * <p>
- * 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.
- * <p>
- * 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.
- * <p>
- * 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 net.jami.model;
-
-import java.io.Serializable;
-import java.util.HashMap;
-import java.util.Map;
-
-public class AccountCredentials implements Serializable {
-
-    @SuppressWarnings("unused")
-    private static final String TAG = AccountCredentials.class.getSimpleName();
-
-    private String mUsername;
-    private String mPassword;
-    private String mRealm;
-
-    public AccountCredentials(Map<String, String> pref) {
-        mUsername = pref.get(ConfigKey.ACCOUNT_USERNAME.key());
-        mPassword = pref.get(ConfigKey.ACCOUNT_PASSWORD.key());
-        mRealm = pref.get(ConfigKey.ACCOUNT_REALM.key());
-    }
-
-    public AccountCredentials(String username, String password, String realm) {
-        setUsername(username);
-        setPassword(password);
-        setRealm(realm);
-    }
-
-    public void setUsername(String val) {
-        mUsername = val;
-    }
-
-    public void setPassword(String val) {
-        mPassword = val;
-    }
-
-    public void setRealm(String val) {
-        mRealm = val;
-    }
-
-    public String getUsername() {
-        return mUsername;
-    }
-
-    public String getPassword() {
-        return mPassword;
-    }
-
-    public String getRealm() {
-        return mRealm;
-    }
-
-    public HashMap<String, String> getDetails() {
-        HashMap<String, String> details = new HashMap<>();
-        details.put(ConfigKey.ACCOUNT_USERNAME.key(), mUsername);
-        details.put(ConfigKey.ACCOUNT_PASSWORD.key(), mPassword);
-        details.put(ConfigKey.ACCOUNT_REALM.key(), mRealm);
-        return details;
-    }
-
-    public void setDetail(ConfigKey key, String value) {
-
-        switch (key) {
-            case ACCOUNT_USERNAME:
-                mUsername = value;
-                break;
-            case ACCOUNT_PASSWORD:
-                mPassword = value;
-                break;
-            case ACCOUNT_REALM:
-                mRealm = value;
-                break;
-        }
-    }
-}
diff --git a/ring-android/libringclient/src/main/java/net/jami/model/AccountCredentials.kt b/ring-android/libringclient/src/main/java/net/jami/model/AccountCredentials.kt
new file mode 100644
index 000000000..2a827a5c4
--- /dev/null
+++ b/ring-android/libringclient/src/main/java/net/jami/model/AccountCredentials.kt
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2004-2016 Savoir-faire Linux Inc.
+ * <p>
+ * Author: Alexandre Lision <alexandre.lision@savoirfairelinux.com>
+ * Adrien Béraud <adrien.beraud@savoirfairelinux.com>
+ * <p>
+ * 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.
+ * <p>
+ * 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.
+ * <p>
+ * 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 net.jami.model
+
+import java.io.Serializable
+import java.util.HashMap
+
+class AccountCredentials : Serializable {
+    var username: String? = null
+    var password: String? = null
+    var realm: String? = null
+
+    constructor(pref: Map<String, String>) {
+        username = pref[ConfigKey.ACCOUNT_USERNAME.key()]
+        password = pref[ConfigKey.ACCOUNT_PASSWORD.key()]
+        realm = pref[ConfigKey.ACCOUNT_REALM.key()]
+    }
+
+    constructor(username: String?, password: String?, realm: String?) {
+        this.username = username
+        this.password = password
+        this.realm = realm
+    }
+
+    val details: HashMap<String, String>
+        get() {
+            val details = HashMap<String, String>()
+            details[ConfigKey.ACCOUNT_USERNAME.key()] = username ?: ""
+            details[ConfigKey.ACCOUNT_PASSWORD.key()] = password ?: ""
+            details[ConfigKey.ACCOUNT_REALM.key()] = realm ?: ""
+            return details
+        }
+
+    fun setDetail(key: ConfigKey, value: String?) {
+        when (key) {
+            ConfigKey.ACCOUNT_USERNAME -> username = value
+            ConfigKey.ACCOUNT_PASSWORD -> password = value
+            ConfigKey.ACCOUNT_REALM -> realm = value
+        }
+    }
+
+    companion object {
+        private val TAG = AccountCredentials::class.java.simpleName
+    }
+}
\ No newline at end of file
diff --git a/ring-android/libringclient/src/main/java/net/jami/model/Codec.java b/ring-android/libringclient/src/main/java/net/jami/model/Codec.java
deleted file mode 100644
index 284ecad55..000000000
--- a/ring-android/libringclient/src/main/java/net/jami/model/Codec.java
+++ /dev/null
@@ -1,108 +0,0 @@
-/*
- *  Copyright (C) 2004-2021 Savoir-faire Linux Inc.
- *
- *  Author: Alexandre Lision <alexandre.lision@savoirfairelinux.com>
- *  Author: Adrien Béraud <adrien.beraud@savoirfairelinux.com>
- *
- *  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, write to the Free Software
- *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
-package net.jami.model;
-
-import java.util.Map;
-
-public class Codec {
-
-    public enum Type {AUDIO, VIDEO}
-
-    private long mPayload;
-    private String mName;
-    private Type mType;
-    private String mSampleRate;
-    private String mBitRate;
-    private String mChannels;
-    private boolean mIsEnabled;
-
-    public Codec(long i, Map<String, String> audioCodecDetails, boolean enabled) {
-        mPayload = i;
-        mName = audioCodecDetails.get("CodecInfo.name");
-        mType = audioCodecDetails.get("CodecInfo.type").contentEquals("AUDIO") ? Type.AUDIO : Type.VIDEO;
-        if (audioCodecDetails.containsKey("CodecInfo.sampleRate")) {
-            mSampleRate = audioCodecDetails.get("CodecInfo.sampleRate");
-        }
-        if (audioCodecDetails.containsKey("CodecInfo.bitrate")) {
-            mBitRate = audioCodecDetails.get("CodecInfo.bitrate");
-        }
-        if (audioCodecDetails.containsKey("CodecInfo.channelNumber")) {
-            mChannels = audioCodecDetails.get("CodecInfo.channelNumber");
-        }
-        mIsEnabled = enabled;
-    }
-
-    @Override
-    public String toString() {
-        return "Codec: " + getName()
-                + "\n" + "Payload: " + getPayload()
-                + "\n" + "Sample Rate: " + getSampleRate()
-                + "\n" + "Bit Rate: " + getBitRate()
-                + "\n" + "Channels: " + getChannels();
-    }
-
-    public Type getType() {
-        return mType;
-    }
-
-    public Long getPayload() {
-        return mPayload;
-    }
-
-    public CharSequence getName() {
-        return mName;
-    }
-
-    public String getSampleRate() {
-        return mSampleRate;
-    }
-
-    public String getBitRate() {
-        return mBitRate;
-    }
-
-    public String getChannels() {
-        return mChannels;
-    }
-
-    public boolean isEnabled() {
-        return mIsEnabled;
-    }
-
-    public void setEnabled(boolean enabled) {
-        mIsEnabled = enabled;
-    }
-
-    public void toggleState() {
-        mIsEnabled = !mIsEnabled;
-    }
-
-    @Override
-    public boolean equals(Object o) {
-        return o instanceof Codec && ((Codec) o).mPayload == mPayload;
-    }
-
-    public boolean isSpeex() {
-        return mName.contentEquals("speex");
-    }
-
-}
diff --git a/ring-android/libringclient/src/main/java/net/jami/model/Codec.kt b/ring-android/libringclient/src/main/java/net/jami/model/Codec.kt
new file mode 100644
index 000000000..86355da3c
--- /dev/null
+++ b/ring-android/libringclient/src/main/java/net/jami/model/Codec.kt
@@ -0,0 +1,57 @@
+/*
+ *  Copyright (C) 2004-2021 Savoir-faire Linux Inc.
+ *
+ *  Author: Alexandre Lision <alexandre.lision@savoirfairelinux.com>
+ *  Author: Adrien Béraud <adrien.beraud@savoirfairelinux.com>
+ *
+ *  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, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+package net.jami.model
+
+class Codec(val payload: Long, audioCodecDetails: Map<String, String>, enabled: Boolean) {
+    enum class Type {
+        AUDIO, VIDEO
+    }
+
+    private val mName: String? = audioCodecDetails["CodecInfo.name"]
+    val type: Type = if (audioCodecDetails["CodecInfo.type"].contentEquals("AUDIO")) Type.AUDIO else Type.VIDEO
+    var sampleRate: String? = audioCodecDetails["CodecInfo.sampleRate"]
+    var bitRate: String? = audioCodecDetails["CodecInfo.bitrate"]
+    var channels: String? = audioCodecDetails["CodecInfo.channelNumber"]
+    var isEnabled: Boolean = enabled
+    override fun toString(): String {
+        return """
+               Codec: $name
+               Payload: $payload
+               Sample Rate: $sampleRate
+               Bit Rate: $bitRate
+               Channels: $channels
+               """.trimIndent()
+    }
+
+    val name: CharSequence?
+        get() = mName
+
+    fun toggleState() {
+        isEnabled = !isEnabled
+    }
+
+    override fun equals(other: Any?): Boolean {
+        return other is Codec && other.payload == payload
+    }
+
+    val isSpeex: Boolean
+        get() = mName.contentEquals("speex")
+}
\ No newline at end of file
diff --git a/ring-android/libringclient/src/main/java/net/jami/model/Contact.kt b/ring-android/libringclient/src/main/java/net/jami/model/Contact.kt
index dcaf93995..549139497 100644
--- a/ring-android/libringclient/src/main/java/net/jami/model/Contact.kt
+++ b/ring-android/libringclient/src/main/java/net/jami/model/Contact.kt
@@ -53,7 +53,6 @@ class Contact private constructor(
     private var mLookupKey: String? = null
     var isUsernameLoaded = false
         private set
-    @JvmField
     var detailsLoaded = false
     private val mConversationUri: BehaviorSubject<Uri> = BehaviorSubject.createDefault(uri)
     var photo: Any? = null
@@ -62,7 +61,6 @@ class Contact private constructor(
     var presenceUpdates: Observable<Boolean>? = null
     private var mContactPresenceEmitter: Emitter<Boolean>? = null
 
-    @JvmOverloads
     constructor(uri: Uri, user: Boolean = false) : this(uri, null, user) {
     }
 
@@ -156,11 +154,11 @@ class Contact private constructor(
         if (!hasNumber(tel)) phones.add(Phone(tel, cat, label))
     }
 
-    fun addNumber(tel: String, cat: Int, label: String?, type: Phone.NumberType?) {
+    fun addNumber(tel: String, cat: Int, label: String?, type: Phone.NumberType) {
         if (!hasNumber(tel)) phones.add(Phone(tel, cat, label, type))
     }
 
-    fun addNumber(tel: Uri, cat: Int, label: String?, type: Phone.NumberType?) {
+    fun addNumber(tel: Uri, cat: Int, label: String?, type: Phone.NumberType) {
         if (!hasNumber(tel)) phones.add(Phone(tel, cat, label, type))
     }
 
@@ -199,6 +197,10 @@ class Contact private constructor(
         }
         return false
     }
+    fun setProfile(profile: Profile?) {
+        if (profile == null) return
+        setProfile(profile.displayName, profile.avatar)
+    }
 
     fun setProfile(name: String?, photo: Any?) {
         if (name != null && name.isNotEmpty() && !name.startsWith(Uri.RING_URI_SCHEME)) {
@@ -217,15 +219,12 @@ class Contact private constructor(
         const val DEFAULT_ID = 0
         const val PREFIX_RING = Uri.RING_URI_SCHEME
 
-        @JvmStatic
         fun buildSIP(to: Uri): Contact {
             val contact = Contact(to)
             contact.isUsernameLoaded = true
             return contact
         }
 
-        @JvmStatic
-        @JvmOverloads
         fun build(uri: String, isUser: Boolean = false): Contact {
             return Contact(Uri.fromString(uri), isUser)
         }
diff --git a/ring-android/libringclient/src/main/java/net/jami/model/Conversation.kt b/ring-android/libringclient/src/main/java/net/jami/model/Conversation.kt
index 6ebbbc7a2..abd8ab682 100644
--- a/ring-android/libringclient/src/main/java/net/jami/model/Conversation.kt
+++ b/ring-android/libringclient/src/main/java/net/jami/model/Conversation.kt
@@ -70,7 +70,7 @@ class Conversation : ConversationHistory {
         this.accountId = accountId
         contacts = mutableListOf(contact)
         uri = contact.uri
-        mParticipant = contact.uri.uri
+        participant = contact.uri.uri
         mContactSubject.onNext(contacts)
         mMode = BehaviorSubject.createDefault(Mode.Legacy)
     }
@@ -640,8 +640,8 @@ class Conversation : ConversationHistory {
     }
 
     interface ConversationActionCallback {
-        fun removeConversation(callContact: Uri)
-        fun clearConversation(callContact: Uri)
+        fun removeConversation(conversationUri: Uri)
+        fun clearConversation(conversationUri: Uri)
         fun copyContactNumberToClipboard(contactNumber: String)
     }
 
diff --git a/ring-android/libringclient/src/main/java/net/jami/model/ConversationHistory.java b/ring-android/libringclient/src/main/java/net/jami/model/ConversationHistory.java
deleted file mode 100644
index 4253dc76d..000000000
--- a/ring-android/libringclient/src/main/java/net/jami/model/ConversationHistory.java
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- *  Copyright (C) 2004-2021 Savoir-faire Linux Inc.
- *
- *  Author: Rayan Osseiran <rayan.osseiran@savoirfairelinux.com>
- *
- *  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, write to the Free Software
- *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-package net.jami.model;
-
-import com.j256.ormlite.field.DatabaseField;
-import com.j256.ormlite.table.DatabaseTable;
-
-@DatabaseTable(tableName = ConversationHistory.TABLE_NAME)
-public class ConversationHistory {
-
-    public static final String TABLE_NAME = "conversations";
-    public static final String COLUMN_CONVERSATION_ID = "id";
-    public static final String COLUMN_PARTICIPANT = "participant";
-    public static final String COLUMN_EXTRA_DATA = "extra_data";
-
-    @DatabaseField(generatedId = true , columnName = COLUMN_CONVERSATION_ID, canBeNull = false)
-    Integer mId;
-    @DatabaseField(columnName = COLUMN_PARTICIPANT, index = true)
-    String mParticipant;
-    @DatabaseField(columnName = COLUMN_EXTRA_DATA)
-    String mExtraData;
-
-    /* Needed by ORMLite */
-    public ConversationHistory() {
-    }
-
-    public ConversationHistory(Conversation conversation) {
-        mId = conversation.getId();
-        mParticipant = conversation.getParticipant();
-    }
-
-    public ConversationHistory(Integer id, String participant) {
-        mId = id;
-        mParticipant = participant;
-    }
-
-    public ConversationHistory(String participant) {
-        mParticipant = participant;
-    }
-
-    public Integer getId() {
-        return mId;
-    }
-
-    public void setId(Integer id) {
-        mId = id;
-    }
-
-    public String getParticipant() {
-        return mParticipant;
-    }
-
-}
diff --git a/ring-android/libringclient/src/main/java/net/jami/model/ConversationHistory.kt b/ring-android/libringclient/src/main/java/net/jami/model/ConversationHistory.kt
new file mode 100644
index 000000000..e1c215578
--- /dev/null
+++ b/ring-android/libringclient/src/main/java/net/jami/model/ConversationHistory.kt
@@ -0,0 +1,58 @@
+/*
+ *  Copyright (C) 2004-2021 Savoir-faire Linux Inc.
+ *
+ *  Author: Rayan Osseiran <rayan.osseiran@savoirfairelinux.com>
+ *
+ *  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, write to the Free Software
+ *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+package net.jami.model
+
+import com.j256.ormlite.table.DatabaseTable
+import com.j256.ormlite.field.DatabaseField
+
+@DatabaseTable(tableName = ConversationHistory.TABLE_NAME)
+open class ConversationHistory {
+    @DatabaseField(generatedId = true, columnName = COLUMN_CONVERSATION_ID, canBeNull = false)
+    var id: Int? = null
+
+    @DatabaseField(columnName = COLUMN_PARTICIPANT, index = true)
+    var participant: String? = null
+
+    @DatabaseField(columnName = COLUMN_EXTRA_DATA)
+    var mExtraData: String? = null
+
+    /* Needed by ORMLite */
+    constructor()
+    constructor(conversation: Conversation) {
+        id = conversation.id
+        participant = conversation.participant
+    }
+
+    constructor(id: Int, participant: String) {
+        this.id = id
+        this.participant = participant
+    }
+
+    constructor(participant: String) {
+        this.participant = participant
+    }
+
+    companion object {
+        const val TABLE_NAME = "conversations"
+        const val COLUMN_CONVERSATION_ID = "id"
+        const val COLUMN_PARTICIPANT = "participant"
+        const val COLUMN_EXTRA_DATA = "extra_data"
+    }
+}
\ No newline at end of file
diff --git a/ring-android/libringclient/src/main/java/net/jami/model/DataTransferError.java b/ring-android/libringclient/src/main/java/net/jami/model/DataTransferError.kt
similarity index 88%
rename from ring-android/libringclient/src/main/java/net/jami/model/DataTransferError.java
rename to ring-android/libringclient/src/main/java/net/jami/model/DataTransferError.kt
index c8c929f35..1a1e83079 100644
--- a/ring-android/libringclient/src/main/java/net/jami/model/DataTransferError.java
+++ b/ring-android/libringclient/src/main/java/net/jami/model/DataTransferError.kt
@@ -17,12 +17,8 @@
  * along with this program; if not, write to the Free Software
  *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
+package net.jami.model
 
-package net.jami.model;
-
-public enum DataTransferError {
-    SUCCESS,
-    UNKNOWN,
-    IO,
-    INVALID_ARGUMENT
+enum class DataTransferError {
+    SUCCESS, UNKNOWN, IO, INVALID_ARGUMENT
 }
\ No newline at end of file
diff --git a/ring-android/libringclient/src/main/java/net/jami/mvp/BaseView.java b/ring-android/libringclient/src/main/java/net/jami/model/Error.kt
similarity index 85%
rename from ring-android/libringclient/src/main/java/net/jami/mvp/BaseView.java
rename to ring-android/libringclient/src/main/java/net/jami/model/Error.kt
index 93faed58e..bf0f77db2 100644
--- a/ring-android/libringclient/src/main/java/net/jami/mvp/BaseView.java
+++ b/ring-android/libringclient/src/main/java/net/jami/model/Error.kt
@@ -1,3 +1,5 @@
+package net.jami.model
+
 /*
  *  Copyright (C) 2004-2021 Savoir-faire Linux Inc.
  *
@@ -17,12 +19,6 @@
  *  along with this program; if not, write to the Free Software
  *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
-package net.jami.mvp;
-
-import net.jami.model.Error;
-
-public interface BaseView {
-
-    void displayErrorToast(Error error);
-
-}
+enum class Error {
+    GENERIC_ERROR, NO_MICROPHONE, NO_INPUT, INVALID_FILE, NOT_ABLE_TO_WRITE_FILE, NO_SPACE_LEFT
+}
\ No newline at end of file
diff --git a/ring-android/libringclient/src/main/java/net/jami/model/Phone.java b/ring-android/libringclient/src/main/java/net/jami/model/Phone.java
deleted file mode 100644
index 355fcc525..000000000
--- a/ring-android/libringclient/src/main/java/net/jami/model/Phone.java
+++ /dev/null
@@ -1,128 +0,0 @@
-/*
- *  Copyright (C) 2004-2021 Savoir-faire Linux Inc.
- *
- *  Author: Alexandre Lision <alexandre.lision@savoirfairelinux.com>
- *
- *  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, write to the Free Software
- *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-package net.jami.model;
-
-public class Phone {
-
-    // TODO: make sure this is usefull since a Uri should already know this
-    private NumberType mNumberType;
-
-    private Uri mNumber;
-    private int mCategory; // Home, work, custom etc.
-    private String mLabel;
-
-    public Phone(Uri number, int category) {
-        mNumberType = NumberType.UNKNOWN;
-        mNumber = number;
-        mLabel = null;
-        mCategory = category;
-    }
-
-    public Phone(String number, int category) {
-        this(number, category, null);
-    }
-
-    public Phone(String number, int category, String label) {
-        mNumberType = NumberType.UNKNOWN;
-        mCategory = category;
-        mNumber = Uri.fromString(number);
-        mLabel = label;
-    }
-    public Phone(Uri number, int category, String label) {
-        mNumberType = NumberType.UNKNOWN;
-        mCategory = category;
-        mNumber = number;
-        mLabel = label;
-    }
-
-    public Phone(String number, int category, String label, NumberType numberType) {
-        mNumberType = numberType;
-        mNumber = Uri.fromString(number);
-        mLabel = label;
-        mCategory = category;
-    }
-    public Phone(Uri number, int category, String label, NumberType numberType) {
-        mNumberType = numberType;
-        mNumber = number;
-        mLabel = label;
-        mCategory = category;
-    }
-
-    public NumberType getType() {
-        return getNumbertype();
-    }
-
-    public void setType(int type) {
-        setNumberType(NumberType.fromInteger(type));
-    }
-
-    public Uri getNumber() {
-        return mNumber;
-    }
-
-    public void setNumber(String number) {
-        setNumber(Uri.fromString(number));
-    }
-
-    public NumberType getNumbertype() {
-        return mNumberType;
-    }
-
-    public void setNumber(Uri number) {
-        this.mNumber = number;
-    }
-
-    public int getCategory() {
-        return mCategory;
-    }
-
-    public String getLabel() {
-        return mLabel;
-    }
-
-    public void setNumberType(NumberType numberType) {
-        this.mNumberType = numberType;
-    }
-
-    public enum NumberType {
-        UNKNOWN(0),
-        TEL(1),
-        SIP(2),
-        IP(2),
-        RING(3);
-
-        private final int type;
-
-        NumberType(int t) {
-            type = t;
-        }
-
-        private static final NumberType[] VALS = NumberType.values();
-
-        public static NumberType fromInteger(int id) {
-            for (NumberType type : VALS) {
-                if (type.type == id) {
-                    return type;
-                }
-            }
-            return UNKNOWN;
-        }
-    }
-}
diff --git a/ring-android/libringclient/src/main/java/net/jami/model/Phone.kt b/ring-android/libringclient/src/main/java/net/jami/model/Phone.kt
new file mode 100644
index 000000000..9153cbbe1
--- /dev/null
+++ b/ring-android/libringclient/src/main/java/net/jami/model/Phone.kt
@@ -0,0 +1,86 @@
+/*
+ *  Copyright (C) 2004-2021 Savoir-faire Linux Inc.
+ *
+ *  Author: Alexandre Lision <alexandre.lision@savoirfairelinux.com>
+ *
+ *  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, write to the Free Software
+ *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+package net.jami.model
+
+import net.jami.model.Uri.Companion.fromString
+import kotlin.jvm.JvmOverloads
+
+class Phone {
+    val numbertype: NumberType
+    val number: Uri
+    // Home, work, custom etc.
+    val category : Int
+    val label: String?
+
+    constructor(number: Uri, category: Int) {
+        numbertype = NumberType.UNKNOWN
+        this.number = number
+        label = null
+        this.category = category
+    }
+
+    @JvmOverloads
+    constructor(number: String?, category: Int, label: String? = null) {
+        numbertype = NumberType.UNKNOWN
+        this.category = category
+        this.number = fromString(number!!)
+        this.label = label
+    }
+
+    constructor(number: Uri, category: Int, label: String?) {
+        numbertype = NumberType.UNKNOWN
+        this.category = category
+        this.number = number
+        this.label = label
+    }
+
+    constructor(number: String?, category: Int, label: String?, numberType: NumberType) {
+        numbertype = numberType
+        this.number = fromString(number!!)
+        this.label = label
+        this.category = category
+    }
+
+    constructor(number: Uri, category: Int, label: String?, numberType: NumberType) {
+        numbertype = numberType
+        this.number = number
+        this.label = label
+        this.category = category
+    }
+
+    val type: NumberType
+        get() = numbertype
+
+    enum class NumberType(private val type: Int) {
+        UNKNOWN(0), TEL(1), SIP(2), IP(2), RING(3);
+
+        companion object {
+            private val VALS = values()
+            fun fromInteger(id: Int): NumberType {
+                for (type in VALS) {
+                    if (type.type == id) {
+                        return type
+                    }
+                }
+                return UNKNOWN
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/ring-android/libringclient/src/main/java/net/jami/model/Profile.kt b/ring-android/libringclient/src/main/java/net/jami/model/Profile.kt
new file mode 100644
index 000000000..ec003b3cb
--- /dev/null
+++ b/ring-android/libringclient/src/main/java/net/jami/model/Profile.kt
@@ -0,0 +1,23 @@
+/*
+ *  Copyright (C) 2004-2021 Savoir-faire Linux Inc.
+ *
+ *  Author: Alexandre Lision <alexandre.lision@savoirfairelinux.com>
+ *  Author: Adrien Béraud <adrien.beraud@savoirfairelinux.com>
+ *  Author: Raphaël Brulé <raphael.brule@savoirfairelinux.com>
+ *
+ *  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 <https://www.gnu.org/licenses/>.
+ */
+package net.jami.model
+
+class Profile(val displayName: String?, val avatar: Any?)
diff --git a/ring-android/libringclient/src/main/java/net/jami/model/Ringtone.java b/ring-android/libringclient/src/main/java/net/jami/model/Ringtone.java
deleted file mode 100644
index 19a31f1f9..000000000
--- a/ring-android/libringclient/src/main/java/net/jami/model/Ringtone.java
+++ /dev/null
@@ -1,79 +0,0 @@
-/*
- *  Copyright (C) 2004-2021 Savoir-faire Linux Inc.
- *
- *  Authors: Rayan Osseiran <rayan.osseiran@savoirfairelinux.com>
- *
- *  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, write to the Free Software
- *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-package net.jami.model;
-
-public class Ringtone {
-
-
-    private String mName, mRingtonePath;
-    private boolean isSelected, isPlaying;
-    private Object mRingtoneIcon;
-
-
-    public Ringtone(String name, String path, Object ringtoneIcon) {
-        mName = name;
-        mRingtonePath = path;
-        isSelected = false;
-        isPlaying = false;
-        mRingtoneIcon = ringtoneIcon;
-    }
-
-    public boolean isSelected() {
-        return isSelected;
-    }
-
-    public void setSelected(boolean selected) {
-        isSelected = selected;
-    }
-
-    public boolean isPlaying() {
-        return isPlaying;
-    }
-
-    public void setPlaying(boolean playing) {
-        isPlaying = playing;
-    }
-
-    public String getName() {
-        return mName;
-    }
-
-    public void setName(String name) {
-        mName = name;
-    }
-
-    public String getRingtonePath() {
-        return mRingtonePath;
-    }
-
-    public void setRingtonePath(String ringtonePath) {
-        mRingtonePath = ringtonePath;
-    }
-
-    public Object getRingtoneIcon() {
-        return mRingtoneIcon;
-    }
-
-    public void setRingtoneIcon(Object ringtoneIcon) {
-        mRingtoneIcon = ringtoneIcon;
-    }
-
-
-}
diff --git a/ring-android/libringclient/src/main/java/net/jami/model/Ringtone.kt b/ring-android/libringclient/src/main/java/net/jami/model/Ringtone.kt
new file mode 100644
index 000000000..2d6e07a4e
--- /dev/null
+++ b/ring-android/libringclient/src/main/java/net/jami/model/Ringtone.kt
@@ -0,0 +1,25 @@
+/*
+ *  Copyright (C) 2004-2021 Savoir-faire Linux Inc.
+ *
+ *  Authors: Rayan Osseiran <rayan.osseiran@savoirfairelinux.com>
+ *
+ *  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, write to the Free Software
+ *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+package net.jami.model
+
+class Ringtone(val name: String, val ringtonePath: String?, val ringtoneIcon: Any) {
+    var isSelected = false
+    var isPlaying = false
+}
\ No newline at end of file
diff --git a/ring-android/libringclient/src/main/java/net/jami/model/Settings.java b/ring-android/libringclient/src/main/java/net/jami/model/Settings.java
deleted file mode 100644
index ad97f2a66..000000000
--- a/ring-android/libringclient/src/main/java/net/jami/model/Settings.java
+++ /dev/null
@@ -1,124 +0,0 @@
-/*
- *  Copyright (C) 2004-2021 Savoir-faire Linux Inc.
- *
- *  Author: Thibault Wittemberg <thibault.wittemberg@savoirfairelinux.com>
- *
- *  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, write to the Free Software
- *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-package net.jami.model;
-
-public class Settings {
-    private boolean mAllowPushNotifications;
-    private boolean mAllowPersistentNotification;
-    private boolean mAllowSystemContacts;
-    private boolean mAllowPlaceSystemCalls;
-    private boolean mAllowOnStartup;
-    private boolean mAllowTypingIndicator;
-    private boolean mAllowReadIndicator;
-    private boolean mBlockRecordIndicator;
-    private boolean mHwEncoding;
-    private int mNotificationVisibility;
-
-    public Settings() {
-    }
-
-    public Settings(Settings s) {
-        if (s != null) {
-            mAllowPushNotifications = s.mAllowPushNotifications;
-            mAllowPersistentNotification = s.mAllowPersistentNotification;
-            mAllowSystemContacts = s.mAllowSystemContacts;
-            mAllowPlaceSystemCalls = s.mAllowPlaceSystemCalls;
-            mAllowOnStartup = s.mAllowOnStartup;
-            mAllowTypingIndicator = s.mAllowTypingIndicator;
-            mAllowReadIndicator = s.mAllowReadIndicator;
-            mBlockRecordIndicator = s.mBlockRecordIndicator;
-            mHwEncoding = s.mHwEncoding;
-            mNotificationVisibility = s.mNotificationVisibility;
-        }
-    }
-
-    public boolean isAllowPushNotifications() {
-        return mAllowPushNotifications;
-    }
-
-    public void setAllowPushNotifications(boolean push) {
-        this.mAllowPushNotifications = push;
-    }
-
-    public boolean isAllowSystemContacts() {
-        return mAllowSystemContacts;
-    }
-
-    public void setAllowSystemContacts(boolean allowSystemContacts) {
-        this.mAllowSystemContacts = allowSystemContacts;
-    }
-
-    public boolean isAllowPlaceSystemCalls() {
-        return mAllowPlaceSystemCalls;
-    }
-
-    public void setAllowPlaceSystemCalls(boolean allowPlaceSystemCalls) {
-        this.mAllowPlaceSystemCalls = allowPlaceSystemCalls;
-    }
-
-    public boolean isAllowOnStartup() {
-        return mAllowOnStartup;
-    }
-
-    public void setAllowRingOnStartup(boolean allowRingOnStartup) {
-        this.mAllowOnStartup = allowRingOnStartup;
-    }
-
-    public void setAllowPersistentNotification(boolean checked) {
-        this.mAllowPersistentNotification = checked;
-    }
-
-    public boolean isAllowPersistentNotification() {
-        return mAllowPersistentNotification;
-    }
-
-    public void setAllowTypingIndicator(boolean checked) {
-        this.mAllowTypingIndicator = checked;
-    }
-
-    public boolean isAllowTypingIndicator() {
-        return mAllowTypingIndicator;
-    }
-
-    public void setAllowReadIndicator(boolean checked) {
-        this.mAllowReadIndicator = checked;
-    }
-
-    public boolean isAllowReadIndicator() {
-        return mAllowReadIndicator;
-    }
-
-    public void setBlockRecordIndicator(boolean checked) {
-        this.mBlockRecordIndicator = checked;
-    }
-
-    public boolean isRecordingBlocked() {
-        return mBlockRecordIndicator;
-    }
-
-    public void setNotificationVisibility(int visibility) {
-        this.mNotificationVisibility = visibility;
-    }
-
-    public int getNotificationVisibility() {
-        return mNotificationVisibility;
-    }
-
-}
diff --git a/ring-android/libringclient/src/main/java/net/jami/model/Settings.kt b/ring-android/libringclient/src/main/java/net/jami/model/Settings.kt
new file mode 100644
index 000000000..e3e84fbc2
--- /dev/null
+++ b/ring-android/libringclient/src/main/java/net/jami/model/Settings.kt
@@ -0,0 +1,58 @@
+/*
+ *  Copyright (C) 2004-2021 Savoir-faire Linux Inc.
+ *
+ *  Author: Thibault Wittemberg <thibault.wittemberg@savoirfairelinux.com>
+ *
+ *  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, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+package net.jami.model
+
+class Settings(s: Settings? = null) {
+    var isAllowPushNotifications = false
+    var isAllowPersistentNotification = false
+    var isAllowSystemContacts = false
+    var isAllowPlaceSystemCalls = false
+    var isAllowOnStartup = false
+        private set
+    var isAllowTypingIndicator = false
+    var isAllowReadIndicator = false
+    var isRecordingBlocked = false
+        private set
+    private var mHwEncoding = false
+    var notificationVisibility = 0
+
+    init {
+        if (s != null) {
+            isAllowPushNotifications = s.isAllowPushNotifications
+            isAllowPersistentNotification = s.isAllowPersistentNotification
+            isAllowSystemContacts = s.isAllowSystemContacts
+            isAllowPlaceSystemCalls = s.isAllowPlaceSystemCalls
+            isAllowOnStartup = s.isAllowOnStartup
+            isAllowTypingIndicator = s.isAllowTypingIndicator
+            isAllowReadIndicator = s.isAllowReadIndicator
+            isRecordingBlocked = s.isRecordingBlocked
+            mHwEncoding = s.mHwEncoding
+            notificationVisibility = s.notificationVisibility
+        }
+    }
+
+    fun setAllowRingOnStartup(allowRingOnStartup: Boolean) {
+        isAllowOnStartup = allowRingOnStartup
+    }
+
+    fun setBlockRecordIndicator(checked: Boolean) {
+        isRecordingBlocked = checked
+    }
+}
\ No newline at end of file
diff --git a/ring-android/libringclient/src/main/java/net/jami/model/Uri.kt b/ring-android/libringclient/src/main/java/net/jami/model/Uri.kt
index 6af5d1303..9f499633a 100644
--- a/ring-android/libringclient/src/main/java/net/jami/model/Uri.kt
+++ b/ring-android/libringclient/src/main/java/net/jami/model/Uri.kt
@@ -120,7 +120,6 @@ class Uri : Serializable {
         private val VALID_IPV4_PATTERN = Pattern.compile(ipv4Pattern, Pattern.CASE_INSENSITIVE)
         private val VALID_IPV6_PATTERN = Pattern.compile(ipv6Pattern, Pattern.CASE_INSENSITIVE)
 
-        @JvmStatic
         fun fromString(uri: String): Uri {
             val m = URI_PATTERN.matcher(uri)
             return if (m.find()) {
@@ -130,7 +129,6 @@ class Uri : Serializable {
             }
         }
 
-        @JvmStatic
         fun fromStringWithName(uriString: String): Tuple<Uri, String?> {
             val m = ANGLE_BRACKETS_PATTERN.matcher(uriString)
             return if (m.find()) {
@@ -140,7 +138,6 @@ class Uri : Serializable {
             }
         }
 
-        @JvmStatic
         fun fromId(conversationId: String): Uri {
             return Uri(null, null, conversationId, null)
         }
@@ -154,7 +151,6 @@ class Uri : Serializable {
          * @return `true` if the string is a value that is a valid IP address,
          * `false` otherwise.
          */
-        @JvmStatic
         fun isIpAddress(ipAddress: String): Boolean {
             val m1 = VALID_IPV4_PATTERN.matcher(ipAddress)
             if (m1.matches()) {
diff --git a/ring-android/libringclient/src/main/java/net/jami/mvp/AccountCreationModel.java b/ring-android/libringclient/src/main/java/net/jami/mvp/AccountCreationModel.java
deleted file mode 100644
index f5ec2f399..000000000
--- a/ring-android/libringclient/src/main/java/net/jami/mvp/AccountCreationModel.java
+++ /dev/null
@@ -1,149 +0,0 @@
-/*
- *  Copyright (C) 2004-2021 Savoir-faire Linux Inc.
- *
- *  Author: Hadrien De Sousa <hadrien.desousa@savoirfairelinux.com>
- *
- *  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, write to the Free Software
- *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-package net.jami.mvp;
-
-import net.jami.model.Account;
-
-import java.io.File;
-import java.io.Serializable;
-
-import ezvcard.VCard;
-import io.reactivex.rxjava3.core.Observable;
-import io.reactivex.rxjava3.core.Single;
-import io.reactivex.rxjava3.subjects.BehaviorSubject;
-import io.reactivex.rxjava3.subjects.Subject;
-
-public abstract class AccountCreationModel implements Serializable {
-
-    private String mManagementServer = null;
-    private String mFullName = "";
-    private String mUsername = "";
-    private String mPassword = "";
-    private String mPin = "";
-    private File mArchive = null;
-
-    private boolean link = false;
-    private boolean mPush = true;
-    transient private net.jami.model.Account newAccount = null;
-    transient private Object photo = null;
-
-    transient private Observable<Account> account;
-    transient protected final Subject<AccountCreationModel> profile = BehaviorSubject.createDefault(this);
-
-    public AccountCreationModel() {
-    }
-
-    public String getFullName() {
-        return mFullName;
-    }
-
-    public void setFullName(String fullName) {
-        this.mFullName = fullName;
-        profile.onNext(this);
-    }
-
-    public Object getPhoto() {
-        return photo;
-    }
-
-    public void setPhoto(Object photo) {
-        this.photo = photo;
-        profile.onNext(this);
-    }
-
-    public String getUsername() {
-        return mUsername;
-    }
-
-    public void setUsername(String username) {
-        this.mUsername = username;
-    }
-
-    public String getPassword() {
-        return mPassword;
-    }
-
-    public void setPassword(String password) {
-        mPassword = password;
-    }
-
-    public String getPin() {
-        return mPin;
-    }
-
-    public void setPin(String pin) {
-        mPin = pin.toUpperCase();
-    }
-
-    public boolean isPush() {
-        return mPush;
-    }
-
-    public void setPush(boolean push) {
-        mPush = push;
-    }
-
-    public boolean isLink() {
-        return link;
-    }
-
-    public void setLink(boolean link) {
-        this.link = link;
-    }
-
-    public void setArchive(File archive) {
-        mArchive = archive;
-    }
-
-    public File getArchive() {
-        return mArchive;
-    }
-
-    public void setManagementServer(String server) {
-        mManagementServer = server;
-    }
-
-    public String getManagementServer() {
-        return mManagementServer;
-    }
-
-    public void setNewAccount(net.jami.model.Account account) {
-        newAccount = account;
-        profile.onNext(this);
-    }
-
-    public net.jami.model.Account getNewAccount() {
-        return newAccount;
-    }
-
-    public void setAccountObservable(Observable<net.jami.model.Account> account) {
-        this.account = account;
-    }
-
-    public Observable<Account> getAccountObservable() {
-        return account;
-    }
-
-    public abstract Single<VCard> toVCard();
-
-    public Observable<AccountCreationModel> getProfileUpdates() {
-        return profile;
-    }
-}
diff --git a/ring-android/libringclient/src/main/java/net/jami/mvp/RootPresenter.java b/ring-android/libringclient/src/main/java/net/jami/mvp/RootPresenter.kt
similarity index 54%
rename from ring-android/libringclient/src/main/java/net/jami/mvp/RootPresenter.java
rename to ring-android/libringclient/src/main/java/net/jami/mvp/RootPresenter.kt
index 8b75cbe6e..47404004c 100644
--- a/ring-android/libringclient/src/main/java/net/jami/mvp/RootPresenter.java
+++ b/ring-android/libringclient/src/main/java/net/jami/mvp/RootPresenter.kt
@@ -17,43 +17,31 @@
  *  along with this program; if not, write to the Free Software
  *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
-package net.jami.mvp;
+package net.jami.mvp
 
-import java.lang.ref.WeakReference;
+import io.reactivex.rxjava3.disposables.CompositeDisposable
+import java.lang.ref.WeakReference
 
-import io.reactivex.rxjava3.disposables.CompositeDisposable;
-
-public abstract class RootPresenter<T> {
-
-    protected CompositeDisposable mCompositeDisposable = new CompositeDisposable();
-
-    public RootPresenter() { }
-
-    private WeakReference<T> mView;
-
-    public void bindView(T view) {
-        mView = new WeakReference<>(view);
+abstract class RootPresenter<T> {
+    @JvmField
+    protected var mCompositeDisposable = CompositeDisposable()
+    private var mView: WeakReference<T>? = null
+    open fun bindView(view: T) {
+        mView = WeakReference(view)
     }
 
-    public void unbindView() {
-        if (mView != null) {
-            mView.clear();
-            mView = null;
+    open fun unbindView() {
+        mView?.let { view ->
+            view.clear()
+            mView = null
         }
-        mCompositeDisposable.clear();
+        mCompositeDisposable.clear()
     }
 
-    public void onDestroy() {
-        mCompositeDisposable.dispose();
+    open fun onDestroy() {
+        mCompositeDisposable.dispose()
     }
 
-    public T getView() {
-        if (mView != null) {
-            return mView.get();
-        }
-        return null;
-    }
-
-}
-
-
+    val view: T?
+        get() = mView?.get()
+}
\ No newline at end of file
diff --git a/ring-android/libringclient/src/main/java/net/jami/navigation/HomeNavigationPresenter.java b/ring-android/libringclient/src/main/java/net/jami/navigation/HomeNavigationPresenter.java
deleted file mode 100644
index 92d31e72d..000000000
--- a/ring-android/libringclient/src/main/java/net/jami/navigation/HomeNavigationPresenter.java
+++ /dev/null
@@ -1,193 +0,0 @@
-/*
- *  Copyright (C) 2004-2021 Savoir-faire Linux Inc.
- *
- *  Author: Aline Bonnet <aline.bonnet@savoirfairelinux.com>
- *  Author: Adrien Béraud <adrien.beraud@savoirfairelinux.com>
- *
- *  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, write to the Free Software
- *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
-package net.jami.navigation;
-
-import java.io.File;
-
-import javax.inject.Inject;
-import javax.inject.Named;
-
-import net.jami.model.Account;
-import net.jami.mvp.RootPresenter;
-import net.jami.services.AccountService;
-import net.jami.services.DeviceRuntimeService;
-import net.jami.services.HardwareService;
-import net.jami.utils.Log;
-import net.jami.utils.StringUtils;
-import net.jami.utils.Tuple;
-import net.jami.utils.VCardUtils;
-import ezvcard.property.Photo;
-import ezvcard.property.RawProperty;
-import ezvcard.property.Uid;
-import io.reactivex.rxjava3.core.Scheduler;
-import io.reactivex.rxjava3.core.Single;
-import io.reactivex.rxjava3.schedulers.Schedulers;
-
-public class HomeNavigationPresenter extends RootPresenter<HomeNavigationView> {
-
-    private static final String TAG = HomeNavigationPresenter.class.getSimpleName();
-
-    private final AccountService mAccountService;
-    private final DeviceRuntimeService mDeviceRuntimeService;
-    private final HardwareService mHardwareService;
-
-    @Inject
-    @Named("UiScheduler")
-    protected Scheduler mUiScheduler;
-
-    @Inject
-    public HomeNavigationPresenter(AccountService accountService,
-                                   HardwareService hardwareService,
-                                   DeviceRuntimeService deviceRuntimeService) {
-        mAccountService = accountService;
-        mHardwareService = hardwareService;
-        mDeviceRuntimeService = deviceRuntimeService;
-    }
-
-    @Override
-    public void bindView(HomeNavigationView view) {
-        super.bindView(view);
-        mCompositeDisposable.add(mAccountService.getCurrentProfileAccountSubject()
-                .switchMap(account -> account.getLoadedProfileObservable()
-                        .map(alias -> new Tuple<>(account, alias)))
-                .observeOn(mUiScheduler)
-                .subscribe(alias -> {
-                    HomeNavigationView v = getView();
-                    if (v != null)
-                        v.showViewModel(new HomeNavigationViewModel(alias.first, alias.second.first));
-                }, e ->  Log.e(TAG, "Error loading account list !", e)));
-        mCompositeDisposable.add(mAccountService.getObservableAccounts()
-                .observeOn(mUiScheduler)
-                .subscribe(account -> {
-                    HomeNavigationView v = getView();
-                    if (v != null)
-                        v.updateModel(account);
-                }, e ->  Log.e(TAG, "Error loading account list !", e)));
-    }
-
-    public void setAccountOrder(Account selectedAccount) {
-        mAccountService.setCurrentAccount(selectedAccount);
-    }
-
-    public void saveVCardPhoto(Single<Photo> photo) {
-        Account account = mAccountService.getCurrentAccount();
-        String accountId = account.getAccountID();
-        String ringId = account.getUsername();
-        File filesDir = mDeviceRuntimeService.provideFilesDir();
-
-        mCompositeDisposable.add(Single.zip(
-                VCardUtils.loadLocalProfileFromDiskWithDefault(filesDir, accountId).subscribeOn(Schedulers.io()),
-                photo.subscribeOn(Schedulers.io()),
-                (vcard, pic) -> {
-                    vcard.setUid(new Uid(ringId));
-                    vcard.removeProperties(Photo.class);
-                    vcard.addPhoto(pic);
-                    vcard.removeProperties(RawProperty.class);
-                    return vcard;
-                })
-                .flatMap(vcard -> VCardUtils.saveLocalProfileToDisk(vcard, accountId, filesDir))
-                .subscribeOn(Schedulers.io())
-                .subscribe(vcard -> {
-                    account.resetProfile();
-                    mAccountService.refreshAccounts();
-                    getView().setPhoto(account);
-                }, e -> Log.e(TAG, "Error saving vCard !", e)));
-    }
-
-    public void saveVCardFormattedName(String username) {
-        Account account = mAccountService.getCurrentAccount();
-        String accountId = account.getAccountID();
-        File filesDir = mDeviceRuntimeService.provideFilesDir();
-
-        mCompositeDisposable.add(VCardUtils
-                .loadLocalProfileFromDiskWithDefault(filesDir, accountId)
-                .doOnSuccess(vcard -> {
-                    vcard.setFormattedName(username);
-                    vcard.removeProperties(RawProperty.class);
-                })
-                .flatMap(vcard -> VCardUtils.saveLocalProfileToDisk(vcard, accountId, filesDir))
-                .subscribeOn(Schedulers.io())
-                .subscribe(vcard -> {
-                    account.resetProfile();
-                    mAccountService.refreshAccounts();
-                    bindView(getView());
-                }, e -> Log.e(TAG, "Error saving vCard !", e)));
-    }
-
-    public void saveVCard(Account account, String username, Single<Photo> photo) {
-        String accountId = account.getAccountID();
-        String ringId = account.getUsername();
-        File filesDir = mDeviceRuntimeService.provideFilesDir();
-        mCompositeDisposable.add(Single.zip(
-                VCardUtils.loadLocalProfileFromDiskWithDefault(filesDir, accountId).subscribeOn(Schedulers.io()),
-                photo, (vcard, pic) -> {
-                    vcard.setUid(new Uid(ringId));
-                    if (!StringUtils.isEmpty(username)) {
-                        vcard.setFormattedName(username);
-                    }
-                    vcard.removeProperties(Photo.class);
-                    vcard.addPhoto(pic);
-                    vcard.removeProperties(RawProperty.class);
-                    return vcard;
-                })
-                .flatMap(vcard -> VCardUtils.saveLocalProfileToDisk(vcard, accountId, filesDir))
-                .subscribeOn(Schedulers.io())
-                .subscribe(vcard -> {
-                    account.resetProfile();
-                    mAccountService.refreshAccounts();
-                }, e -> Log.e(TAG, "Error saving vCard !", e)));
-    }
-
-    public String getUri(Account account, CharSequence defaultNameSip) {
-        if (account.isIP2IP()) {
-            return defaultNameSip.toString();
-        }
-        return account.getDisplayUri();
-    }
-
-    public void cameraClicked() {
-        boolean hasPermission = mDeviceRuntimeService.hasVideoPermission() &&
-                mDeviceRuntimeService.hasWriteExternalStoragePermission();
-        if (hasPermission) {
-            getView().gotToImageCapture();
-        } else {
-            getView().askCameraPermission();
-        }
-    }
-
-    public void galleryClicked() {
-        boolean hasPermission = mDeviceRuntimeService.hasGalleryPermission();
-        if (hasPermission) {
-            getView().goToGallery();
-        } else {
-            getView().askGalleryPermission();
-        }
-    }
-
-    public void cameraPermissionChanged(boolean isGranted) {
-        if (isGranted && mHardwareService.isVideoAvailable()) {
-            mHardwareService.initVideo()
-                    .onErrorComplete()
-                    .subscribe();
-        }
-    }
-}
diff --git a/ring-android/libringclient/src/main/java/net/jami/navigation/HomeNavigationPresenter.kt b/ring-android/libringclient/src/main/java/net/jami/navigation/HomeNavigationPresenter.kt
new file mode 100644
index 000000000..21b129d2d
--- /dev/null
+++ b/ring-android/libringclient/src/main/java/net/jami/navigation/HomeNavigationPresenter.kt
@@ -0,0 +1,160 @@
+/*
+ *  Copyright (C) 2004-2021 Savoir-faire Linux Inc.
+ *
+ *  Author: Aline Bonnet <aline.bonnet@savoirfairelinux.com>
+ *  Author: Adrien Béraud <adrien.beraud@savoirfairelinux.com>
+ *
+ *  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, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+package net.jami.navigation
+
+import ezvcard.VCard
+import ezvcard.property.Photo
+import ezvcard.property.RawProperty
+import ezvcard.property.Uid
+import io.reactivex.rxjava3.core.Scheduler
+import io.reactivex.rxjava3.core.Single
+import io.reactivex.rxjava3.schedulers.Schedulers
+import net.jami.model.Account
+import net.jami.mvp.RootPresenter
+import net.jami.services.AccountService
+import net.jami.services.DeviceRuntimeService
+import net.jami.services.HardwareService
+import net.jami.services.VCardService
+import net.jami.utils.Log
+import net.jami.utils.StringUtils.isEmpty
+import net.jami.utils.VCardUtils
+import javax.inject.Inject
+import javax.inject.Named
+
+class HomeNavigationPresenter @Inject constructor(
+    private val mAccountService: AccountService,
+    private val mHardwareService: HardwareService,
+    private val mDeviceRuntimeService: DeviceRuntimeService,
+    private val mVCardService: VCardService,
+    @param:Named("UiScheduler")
+    private val mUiScheduler: Scheduler
+) : RootPresenter<HomeNavigationView>() {
+
+    override fun bindView(view: HomeNavigationView) {
+        super.bindView(view)
+        mCompositeDisposable.add(mAccountService.currentProfileAccountSubject
+            .observeOn(mUiScheduler)
+            .subscribe({ accountProfile ->
+                this.view?.showViewModel(HomeNavigationViewModel(accountProfile.first, accountProfile.second))
+            }) { e: Throwable -> Log.e(TAG, "Error loading account list !", e) })
+    }
+
+    fun saveVCardPhoto(photo: Single<Photo>) {
+        val account = mAccountService.currentAccount!!
+        val accountId = account.accountID
+        val ringId = account.username
+        val filesDir = mDeviceRuntimeService.provideFilesDir()
+        mCompositeDisposable.add(Single.zip(
+            VCardUtils.loadLocalProfileFromDiskWithDefault(filesDir, accountId).subscribeOn(Schedulers.io()),
+            photo.subscribeOn(Schedulers.io()),
+            { vcard: VCard, pic: Photo ->
+                vcard.uid = Uid(ringId)
+                vcard.removeProperties(Photo::class.java)
+                vcard.addPhoto(pic)
+                vcard.removeProperties(RawProperty::class.java)
+                vcard
+            })
+            .subscribeOn(Schedulers.io())
+            .subscribe({ vcard ->
+                account.loadedProfile = mVCardService.loadVCardProfile(vcard).cache()
+                VCardUtils.saveLocalProfileToDisk(vcard, accountId, filesDir)
+                    .subscribeOn(Schedulers.io())
+                    .subscribe()
+            }) { e: Throwable -> Log.e(TAG, "Error saving vCard !", e) })
+    }
+
+    fun saveVCardFormattedName(username: String?) {
+        val account = mAccountService.currentAccount!!
+        val accountId = account.accountID
+        val filesDir = mDeviceRuntimeService.provideFilesDir()
+        mCompositeDisposable.add(VCardUtils.loadLocalProfileFromDiskWithDefault(filesDir, accountId)
+            .doOnSuccess { vcard: VCard ->
+                vcard.setFormattedName(username)
+                vcard.removeProperties(RawProperty::class.java)
+            }
+            .subscribeOn(Schedulers.io())
+            .subscribe({ vcard ->
+                account.loadedProfile = mVCardService.loadVCardProfile(vcard).cache()
+                VCardUtils.saveLocalProfileToDisk(vcard, accountId, filesDir)
+                    .subscribeOn(Schedulers.io())
+                    .subscribe()
+            }) { e: Throwable -> Log.e(TAG, "Error saving vCard !", e) })
+    }
+
+    fun saveVCard(account: Account, username: String?, photo: Single<Photo>) {
+        val accountId = account.accountID
+        val ringId = account.username
+        val filesDir = mDeviceRuntimeService.provideFilesDir()
+        mCompositeDisposable.add(Single.zip(
+            VCardUtils.loadLocalProfileFromDiskWithDefault(filesDir, accountId).subscribeOn(Schedulers.io()),
+            photo, { vcard: VCard, pic: Photo ->
+                vcard.uid = Uid(ringId)
+                if (!isEmpty(username)) {
+                    vcard.setFormattedName(username)
+                }
+                vcard.removeProperties(Photo::class.java)
+                vcard.addPhoto(pic)
+                vcard.removeProperties(RawProperty::class.java)
+                account.loadedProfile = mVCardService.loadVCardProfile(vcard).cache()
+                vcard
+            })
+            .flatMap { vcard -> VCardUtils.saveLocalProfileToDisk(vcard, accountId, filesDir) }
+            .subscribeOn(Schedulers.io())
+            .subscribe({}) { e: Throwable -> Log.e(TAG, "Error saving vCard !", e) })
+    }
+
+    fun getUri(account: Account, defaultNameSip: CharSequence): String? {
+        return if (account.isIP2IP) {
+            defaultNameSip.toString()
+        } else account.displayUri
+    }
+
+    fun cameraClicked() {
+        val hasPermission = mDeviceRuntimeService.hasVideoPermission() &&
+                mDeviceRuntimeService.hasWriteExternalStoragePermission()
+        if (hasPermission) {
+            view?.gotToImageCapture()
+        } else {
+            view?.askCameraPermission()
+        }
+    }
+
+    fun galleryClicked() {
+        val hasPermission = mDeviceRuntimeService.hasGalleryPermission()
+        if (hasPermission) {
+            view?.goToGallery()
+        } else {
+            view?.askGalleryPermission()
+        }
+    }
+
+    fun cameraPermissionChanged(isGranted: Boolean) {
+        if (isGranted && mHardwareService.isVideoAvailable) {
+            mHardwareService.initVideo()
+                .onErrorComplete()
+                .subscribe()
+        }
+    }
+
+    companion object {
+        private val TAG = HomeNavigationPresenter::class.simpleName!!
+    }
+}
\ No newline at end of file
diff --git a/ring-android/libringclient/src/main/java/net/jami/navigation/HomeNavigationView.java b/ring-android/libringclient/src/main/java/net/jami/navigation/HomeNavigationView.java
deleted file mode 100644
index fc87c0309..000000000
--- a/ring-android/libringclient/src/main/java/net/jami/navigation/HomeNavigationView.java
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- *  Copyright (C) 2004-2021 Savoir-faire Linux Inc.
- *
- *  Author: Hadrien De Sousa <hadrien.desousa@savoirfairelinux.com>
- *
- *  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, write to the Free Software
- *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-package net.jami.navigation;
-
-import net.jami.model.Account;
-
-public interface HomeNavigationView {
-
-    void showViewModel(HomeNavigationViewModel viewModel);
-
-    void updateModel(Account account);
-
-    void gotToImageCapture();
-
-    void askCameraPermission();
-
-    void goToGallery();
-
-    void askGalleryPermission();
-
-    void setPhoto(Account account);
-
-}
diff --git a/ring-android/libringclient/src/main/java/net/jami/account/JamiLinkAccountView.java b/ring-android/libringclient/src/main/java/net/jami/navigation/HomeNavigationView.kt
similarity index 74%
rename from ring-android/libringclient/src/main/java/net/jami/account/JamiLinkAccountView.java
rename to ring-android/libringclient/src/main/java/net/jami/navigation/HomeNavigationView.kt
index 67b13655f..3c8f35144 100644
--- a/ring-android/libringclient/src/main/java/net/jami/account/JamiLinkAccountView.java
+++ b/ring-android/libringclient/src/main/java/net/jami/navigation/HomeNavigationView.kt
@@ -17,17 +17,15 @@
  *  along with this program; if not, write to the Free Software
  *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
-package net.jami.account;
+package net.jami.navigation
 
-import net.jami.mvp.AccountCreationModel;
+import net.jami.model.Account
 
-public interface JamiLinkAccountView {
-
-    void enableLinkButton(boolean enable);
-
-    void showPin(boolean show);
-
-    void createAccount(AccountCreationModel accountCreationModel);
-
-    void cancel();
-}
+interface HomeNavigationView {
+    fun showViewModel(viewModel: HomeNavigationViewModel)
+    fun gotToImageCapture()
+    fun askCameraPermission()
+    fun goToGallery()
+    fun askGalleryPermission()
+    //fun setPhoto(account: Account)
+}
\ No newline at end of file
diff --git a/ring-android/libringclient/src/main/java/net/jami/navigation/HomeNavigationViewModel.kt b/ring-android/libringclient/src/main/java/net/jami/navigation/HomeNavigationViewModel.kt
index 6589fd507..9300f15ff 100644
--- a/ring-android/libringclient/src/main/java/net/jami/navigation/HomeNavigationViewModel.kt
+++ b/ring-android/libringclient/src/main/java/net/jami/navigation/HomeNavigationViewModel.kt
@@ -21,5 +21,6 @@
 package net.jami.navigation
 
 import net.jami.model.Account
+import net.jami.model.Profile
 
-class HomeNavigationViewModel(val account: Account, val alias: String?)
\ No newline at end of file
+class HomeNavigationViewModel(val account: Account, val profile: Profile)
\ No newline at end of file
diff --git a/ring-android/libringclient/src/main/java/net/jami/services/AccountService.kt b/ring-android/libringclient/src/main/java/net/jami/services/AccountService.kt
index 31c70d754..f14c066f2 100644
--- a/ring-android/libringclient/src/main/java/net/jami/services/AccountService.kt
+++ b/ring-android/libringclient/src/main/java/net/jami/services/AccountService.kt
@@ -68,6 +68,10 @@ class AccountService(
      */
     var currentAccount: Account? = null
         set(account) {
+            if (field == null) {
+                field = account
+                return
+            }
             if (field === account) return
             field = account!!
 
@@ -78,7 +82,7 @@ class AccountService(
             val selectedID = account.accountID
             orderedAccountIdList.add(selectedID)
             for (a in accounts) {
-                if (a.accountID.contentEquals(selectedID)) {
+                if (a.accountID == selectedID) {
                     continue
                 }
                 orderedAccountIdList.add(a.accountID)
@@ -336,18 +340,15 @@ class AccountService(
                     }
                     if (enabled) {
                         for (contact in account.contacts.values) {
-                            if (!contact.isUsernameLoaded) JamiService.lookupAddress(
-                                accountId,
-                                "",
-                                contact.uri.rawRingId
-                            )
+                            if (!contact.isUsernameLoaded)
+                                JamiService.lookupAddress(accountId, "", contact.uri.rawRingId)
                         }
                     }
                 }
             }
             mHasSipAccount = hasSip
             mHasRingAccount = hasJami
-            if (!newAccounts.isEmpty()) {
+            if (newAccounts.isNotEmpty()) {
                 val newAccount = newAccounts[0]
                 if (currentAccount !== newAccount) {
                     currentAccount = newAccount
@@ -386,7 +387,7 @@ class AccountService(
      * @param map the account details
      * @return the created Account
      */
-    fun addAccount(map: Map<String?, String?>?): Observable<Account?> {
+    fun addAccount(map: Map<String, String>): Observable<Account> {
         return Observable.fromCallable {
             val accountId = JamiService.addAccount(StringMap.toSwig(map))
             if (StringUtils.isEmpty(accountId)) {
@@ -450,6 +451,12 @@ class AccountService(
         return observableAccounts.filter { acc -> acc.accountID == accountId }
     }
 
+    fun getObservableAccountProfile(accountId: String): Observable<Pair<Account, Profile>> {
+        return getObservableAccount(accountId).flatMap { a: Account ->
+            mVCardService.loadProfile(a).map { profile -> Pair(a, profile) }
+        }
+    }
+
     fun getObservableAccount(accountId: String): Observable<Account> {
         return Observable.fromCallable<Account> { getAccount(accountId) }
             .concatWith(getObservableAccountUpdates(accountId))
@@ -460,9 +467,9 @@ class AccountService(
             .concatWith(observableAccounts.filter { acc -> acc === account })
     }
 
-    val currentProfileAccountSubject: Observable<Account>
-        get() = currentAccountSubject.flatMapSingle { a: Account ->
-            mVCardService.loadProfile(a).firstOrError().map { p: Tuple<String?, Any?>? -> a }
+    val currentProfileAccountSubject: Observable<Pair<Account, Profile>>
+        get() = currentAccountSubject.flatMap { a: Account ->
+            mVCardService.loadProfile(a).map { profile -> Pair(a, profile) }
         }
 
     fun subscribeBuddy(accountID: String?, uri: String?, flag: Boolean) {
@@ -571,7 +578,7 @@ class AccountService(
      *
      * @param accountOrder The ordered list of account ids
      */
-    fun setAccountOrder(accountOrder: List<String>) {
+    private fun setAccountOrder(accountOrder: List<String>) {
         mExecutor.execute {
             val order = StringBuilder()
             for (accountId in accountOrder) {
@@ -1116,17 +1123,24 @@ class AccountService(
         val account = getAccount(accountId) ?: return
         mVCardService.saveVCardProfile(accountId, account.uri, name, photo)
             .subscribeOn(Schedulers.io())
-            .subscribe({ account.resetProfile() }) { e -> Log.e(TAG, "Error saving profile", e) }
+            .subscribe({ vcard -> account.loadedProfile = mVCardService.loadVCardProfile(vcard).cache() })
+                { e -> Log.e(TAG, "Error saving profile", e) }
     }
 
     fun profileReceived(accountId: String, peerId: String, vcardPath: String) {
         val account = getAccount(accountId) ?: return
         Log.w(TAG, "profileReceived: $accountId, $peerId, $vcardPath")
         val contact = account.getContactFromCache(peerId)
-        mVCardService.peerProfileReceived(accountId, peerId, File(vcardPath))
-            .subscribe({ profile: Tuple<String?, Any?> ->
-                contact.setProfile(profile.first, profile.second)
-            }) { e -> Log.e(TAG, "Error saving contact profile", e) }
+        if (contact.isUser) {
+            mVCardService.accountProfileReceived(accountId, File(vcardPath))
+                .subscribe({ profile: Profile ->
+                    account.loadedProfile = Single.just(profile)
+                }) { e -> Log.e(TAG, "Error saving contact profile", e) }
+        } else {
+            mVCardService.peerProfileReceived(accountId, peerId, File(vcardPath))
+                .subscribe({ profile -> contact.setProfile(profile) })
+                    { e -> Log.e(TAG, "Error saving contact profile", e) }
+        }
     }
 
     fun incomingAccountMessage(accountId: String, messageId: String?, callId: String?, from: String, messages: Map<String, String>) {
@@ -1242,12 +1256,7 @@ class AccountService(
                     // VCardUtils.savePeerProfileToDisk(vcard, accountId, from + ".vcf", mDeviceRuntimeService.provideFilesDir());
                     mVCardService.loadVCardProfile(vcard)
                         .subscribeOn(Schedulers.computation())
-                        .subscribe { profile: Tuple<String?, Any?> ->
-                            contact.setProfile(
-                                profile.first,
-                                profile.second
-                            )
-                        }
+                        .subscribe { profile -> contact.setProfile(profile) }
                 }
             }
             account.addRequest(request)
@@ -1258,8 +1267,7 @@ class AccountService(
     }
 
     fun contactAdded(accountId: String, uri: String, confirmed: Boolean) {
-        val account = getAccount(accountId)
-        if (account != null) {
+        getAccount(accountId)?.let { account ->
             val details: Map<String, String> = JamiService.getContactDetails(accountId, uri)
             val contact = account.addContact(details)
             val conversationUri = contact.conversationUri.blockingFirst()
diff --git a/ring-android/libringclient/src/main/java/net/jami/services/CallService.kt b/ring-android/libringclient/src/main/java/net/jami/services/CallService.kt
index 485098353..f665ed471 100644
--- a/ring-android/libringclient/src/main/java/net/jami/services/CallService.kt
+++ b/ring-android/libringclient/src/main/java/net/jami/services/CallService.kt
@@ -33,8 +33,6 @@ import net.jami.model.Call.CallStatus
 import net.jami.model.Conference
 import net.jami.model.Conference.ParticipantInfo
 import net.jami.model.Uri
-import net.jami.model.Uri.Companion.fromString
-import net.jami.model.Uri.Companion.fromStringWithName
 import net.jami.utils.Log
 import net.jami.utils.StringUtils.isEmpty
 import java.util.*
@@ -97,7 +95,7 @@ class CallService(
             val newInfo: MutableList<ParticipantInfo> = ArrayList(info.size)
             if (conference.isConference) {
                 for (i in info) {
-                    val call = conference.findCallByContact(fromString(i["uri"]!!))
+                    val call = conference.findCallByContact(Uri.fromString(i["uri"]!!))
                     if (call != null) {
                         val confInfo = ParticipantInfo(call, call.contact!!, i)
                         if (confInfo.isEmpty) {
@@ -116,7 +114,7 @@ class CallService(
             } else {
                 val account = mAccountService.getAccount(conference.call!!.account!!)!!
                 for (i in info) {
-                    val confInfo = ParticipantInfo(null, account.getContactFromCache(fromString(i["uri"]!!)), i)
+                    val confInfo = ParticipantInfo(null, account.getContactFromCache(Uri.fromString(i["uri"]!!)), i)
                     if (confInfo.isEmpty) {
                         Log.w(TAG, "onConferenceInfoUpdated: ignoring empty entry $i")
                         continue
@@ -136,7 +134,7 @@ class CallService(
 
     fun setConfMaximizedParticipant(confId: String, uri: Uri) {
         mExecutor.execute {
-            JamiService.setActiveParticipant(confId, uri?.rawRingId ?: "")
+            JamiService.setActiveParticipant(confId, uri.rawRingId ?: "")
             JamiService.setConferenceLayout(confId, 1)
         }
     }
@@ -248,6 +246,7 @@ class CallService(
     }
 
     fun hangUp(callId: String) {
+        Log.i(TAG, "hangUp() called... $callId", Exception())
         mExecutor.execute {
             Log.i(TAG, "hangUp() running... $callId")
             JamiService.hangUp(callId)
@@ -385,7 +384,7 @@ class CallService(
             mExecutor.execute { JamiService.setRecordPath(path) }
         }
 
-    fun toggleRecordingCall(id: String?): Boolean {
+    fun toggleRecordingCall(id: String): Boolean {
         mExecutor.execute { JamiService.toggleRecording(id) }
         return false
     }
@@ -399,7 +398,7 @@ class CallService(
         mExecutor.execute { JamiService.stopRecordedFilePlayback() }
     }
 
-    fun sendTextMessage(callId: String, msg: String?) {
+    fun sendTextMessage(callId: String, msg: String) {
         mExecutor.execute {
             Log.i(TAG, "sendTextMessage() thread running...")
             val messages = StringMap()
@@ -487,9 +486,9 @@ class CallService(
             }
             call.setCallState(callState)
             val account = mAccountService.getAccount(call.account!!)!!
-            val contact = mContactService.findContact(account, fromString(call.contactNumber!!))
+            val contact = mContactService.findContact(account, Uri.fromString(call.contactNumber!!))
             val registeredName = callDetails[Call.KEY_REGISTERED_NAME]
-            if (registeredName != null && !registeredName.isEmpty()) {
+            if (registeredName != null && registeredName.isNotEmpty()) {
                 contact.setUsername(registeredName)
             }
             val conversation = account.getByUri(contact.conversationUri.blockingFirst())
@@ -536,7 +535,7 @@ class CallService(
 
     fun incomingCall(accountId: String, callId: String, from: String) {
         Log.d(TAG, "incoming call: $accountId, $callId, $from")
-        val call = addCall(accountId, callId, fromStringWithName(from).first, Call.Direction.INCOMING)
+        val call = addCall(accountId, callId, Uri.fromStringWithName(from).first, Call.Direction.INCOMING)
         callSubject.onNext(call)
         updateConnectionCount()
     }
@@ -720,8 +719,7 @@ class CallService(
 
     fun conferenceRemoved(confId: String) {
         Log.d(TAG, "conference removed: $confId")
-        val conf = currentConferences.remove(confId)
-        if (conf != null) {
+        currentConferences.remove(confId)?.let { conf ->
             for (call in conf.participants) {
                 call.confId = null
             }
diff --git a/ring-android/libringclient/src/main/java/net/jami/services/ContactService.kt b/ring-android/libringclient/src/main/java/net/jami/services/ContactService.kt
index 755ea2e3c..9d1b17422 100644
--- a/ring-android/libringclient/src/main/java/net/jami/services/ContactService.kt
+++ b/ring-android/libringclient/src/main/java/net/jami/services/ContactService.kt
@@ -49,7 +49,7 @@ abstract class ContactService(
     protected abstract fun findContactByNumberFromSystem(number: String): Contact?
     abstract fun loadContactData(contact: Contact, accountId: String): Completable
     abstract fun saveVCardContactData(contact: Contact, accountId: String, vcard: VCard)
-    abstract fun saveVCardContact(accountId: String, uri: String, displayName: String, pictureB64: String): Single<VCard>
+    abstract fun saveVCardContact(accountId: String, uri: String?, displayName: String?, pictureB64: String?): Single<VCard>
 
     /**
      * Load contacts from system and generate a local contact cache
@@ -99,9 +99,7 @@ abstract class ContactService(
                     .replay(1)
                     .refCount()
             }
-            return if (withPresence) Observable.combineLatest(
-                contact.updates,
-                contact.presenceUpdates,
+            return if (withPresence) Observable.combineLatest(contact.updates, contact.presenceUpdates,
                 { c: Contact, p: Boolean -> c }) else contact.updates!!
         }
     }
diff --git a/ring-android/libringclient/src/main/java/net/jami/services/ConversationFacade.kt b/ring-android/libringclient/src/main/java/net/jami/services/ConversationFacade.kt
index 1503eca77..c759e5494 100644
--- a/ring-android/libringclient/src/main/java/net/jami/services/ConversationFacade.kt
+++ b/ring-android/libringclient/src/main/java/net/jami/services/ConversationFacade.kt
@@ -36,7 +36,6 @@ import net.jami.services.AccountService.RegisteredName
 import net.jami.smartlist.SmartListViewModel
 import net.jami.utils.FileUtils.moveFile
 import net.jami.utils.Log
-import net.jami.utils.Tuple
 import java.io.File
 import java.util.*
 import java.util.concurrent.TimeUnit
@@ -129,9 +128,9 @@ class ConversationFacade(
             }.ignoreElement()
     }
 
-    fun sendTextMessage(c: Conversation, conf: Conference, txt: String?) {
+    fun sendTextMessage(c: Conversation, conf: Conference, txt: String) {
         mCallService.sendTextMessage(conf.id, txt)
-        val message = TextMessage(null, c.accountId, conf.id, c, txt!!)
+        val message = TextMessage(null, c.accountId, conf.id, c, txt)
         message.read()
         mHistoryService.insertInteraction(c.accountId, c, message).subscribe()
         c.addTextMessage(message)
@@ -152,29 +151,18 @@ class ConversationFacade(
             return Completable.complete()
         }
         return Single.fromCallable {
-            val transfer = DataTransfer(
-                conversation,
-                to.rawRingId,
-                conversation.accountId,
-                file.name,
-                true,
-                file.length(),
-                0,
-                null
-            )
+            val transfer = DataTransfer(conversation, to.rawRingId, conversation.accountId, file.name, true, file.length(), 0, null)
             mHistoryService.insertInteraction(conversation.accountId, conversation, transfer).blockingAwait()
             transfer.destination = mDeviceRuntimeService.getConversationDir(conversation.uri.rawRingId)
             transfer
         }
             .flatMap { t: DataTransfer -> mAccountService.sendFile(file, t) }
-            .flatMapCompletable { transfer: DataTransfer ->
-                Completable.fromAction {
-                    val destination = File(transfer.destination, transfer.storagePath)
-                    if (!mDeviceRuntimeService.hardLinkOrCopy(file, destination)) {
-                        Log.e(TAG, "sendFile: can't move file to $destination")
-                    }
+            .flatMapCompletable { transfer: DataTransfer -> Completable.fromAction {
+                val destination = File(transfer.destination, transfer.storagePath)
+                if (!mDeviceRuntimeService.hardLinkOrCopy(file, destination)) {
+                    Log.e(TAG, "sendFile: can't move file to $destination")
                 }
-            }
+            } }
             .subscribeOn(Schedulers.io())
     }
 
@@ -210,9 +198,9 @@ class ConversationFacade(
         val accountId = message.account!!
         mDisposableBag.add(
             Completable.mergeArrayDelayError(mCallService.cancelMessage(accountId, message.id.toLong()).subscribeOn(Schedulers.io()))
-                .andThen(startConversation(accountId, fromString(message.conversation!!.participant)))
+                .andThen(startConversation(accountId, fromString(message.conversation!!.participant!!)))
                 .subscribe({ c: Conversation -> c.removeInteraction(message) }
-                ) { e: Throwable? -> Log.e(TAG, "Can't cancel message sending", e) })
+                ) { e: Throwable -> Log.e(TAG, "Can't cancel message sending", e) })
     }
 
     /**
@@ -273,19 +261,20 @@ class ConversationFacade(
     fun getSmartList(currentAccount: Observable<Account>, hasPresence: Boolean): Observable<List<Observable<SmartListViewModel>>> {
         return currentAccount.switchMap { account: Account ->
             account.getConversationsSubject()
-                .switchMapSingle { conversations -> Observable.fromIterable(conversations)
+                .switchMapSingle { conversations -> if (conversations.isEmpty()) Single.just(emptyList())
+                else Observable.fromIterable(conversations)
                     .map { conversation: Conversation -> observeConversation(account, conversation, hasPresence) }
-                    .toList() } }
+                    .toList(conversations.size) } }
     }
 
-    fun getContactList(currentAccount: Observable<Account>): Observable<List<SmartListViewModel>> {
+    fun getContactList(currentAccount: Observable<Account>): Observable<MutableList<SmartListViewModel>> {
         return currentAccount.switchMap { account: Account ->
             account.getConversationsSubject()
-                .switchMapSingle { conversations: List<Conversation>? ->
-                    Observable.fromIterable(conversations)
+                .switchMapSingle { conversations: List<Conversation> -> if (conversations.isEmpty()) Single.just(ArrayList())
+                else Observable.fromIterable(conversations)
                         .filter { conversation: Conversation -> !conversation.isSwarm }
                         .map { conversation: Conversation -> SmartListViewModel(conversation, false) }
-                        .toList()
+                        .toList(conversations.size)
                 }
         }
     }
@@ -304,7 +293,7 @@ class ConversationFacade(
 
     val pendingList: Observable<List<Observable<SmartListViewModel>>>
         get() = getPendingList(mAccountService.currentAccountSubject)
-    val contactList: Observable<List<SmartListViewModel>>
+    val contactList: Observable<MutableList<SmartListViewModel>>
         get() = getContactList(mAccountService.currentAccountSubject)
 
     private fun getSearchResults(account: Account, query: String): Single<List<Observable<SmartListViewModel>>> {
@@ -409,7 +398,7 @@ class ConversationFacade(
      */
     private fun getConversationHistory(conversation: Conversation): Single<Conversation> {
         Log.d(TAG, "getConversationHistory() " + conversation.accountId + " " + conversation.uri)
-        return mHistoryService.getConversationHistory(conversation.accountId, conversation.id)
+        return mHistoryService.getConversationHistory(conversation.accountId, conversation.id!!)
             .map { loadedConversation: List<Interaction> ->
                 conversation.clearHistory(true)
                 conversation.setHistory(loadedConversation)
@@ -464,7 +453,7 @@ class ConversationFacade(
             .firstOrError()
             .subscribeOn(Schedulers.io())
             .subscribe({ c: List<Conversation> -> updateTextNotifications(txt.account!!, c) })
-            { e: Throwable -> Log.e(TAG, e.message) }
+            { e: Throwable -> Log.e(TAG, e.message!!) }
     }
 
     fun acceptRequest(accountId: String, contactUri: Uri) {
@@ -479,11 +468,12 @@ class ConversationFacade(
     }
 
     private fun handleDataTransferEvent(transfer: DataTransfer) {
-        val conversation = mAccountService.getAccount(transfer.account!!)!!.onDataTransferEvent(transfer)
+        val account = transfer.account!!
+        val conversation = mAccountService.getAccount(account)!!.onDataTransferEvent(transfer)
         val status = transfer.status
-        Log.d(TAG, "handleDataTransferEvent $status " + transfer.canAutoAccept(mPreferencesService.getMaxFileAutoAccept(transfer.account)))
+        Log.d(TAG, "handleDataTransferEvent $status " + transfer.canAutoAccept(mPreferencesService.getMaxFileAutoAccept(account)))
         if (status === InteractionStatus.TRANSFER_AWAITING_HOST || status === InteractionStatus.FILE_AVAILABLE) {
-            if (transfer.canAutoAccept(mPreferencesService.getMaxFileAutoAccept(transfer.account))) {
+            if (transfer.canAutoAccept(mPreferencesService.getMaxFileAutoAccept(account))) {
                 mAccountService.acceptFileTransfer(conversation, transfer.fileId!!, transfer)
                 return
             }
@@ -505,18 +495,13 @@ class ConversationFacade(
         val conversationId = call.conversationId
         Log.w(TAG, "CallStateChange " + call.daemonIdString + " conversationId:" + conversationId)
         val conversation = if (conversationId == null)
-            if (contact == null) null else account.getByUri(contact.uri)
+            if (contact == null) return else account.getByUri(contact.uri)!!
         else
-            account.getSwarm(conversationId)
-        var conference: Conference? = null
-        if (conversation != null) {
-            conference = conversation.getConference(call.daemonIdString)
-            if (conference == null) {
-                if (newState === CallStatus.OVER) return
-                conference = Conference(call)
-                conversation.addConference(conference)
-                account.updated(conversation)
-            }
+            account.getSwarm(conversationId) ?: return
+        val conference: Conference = conversation.getConference(call.daemonIdString) ?: Conference(call).apply {
+            if (newState === CallStatus.OVER) return@onCallStateChange
+            conversation.addConference(this)
+            account.updated(conversation)
         }
         Log.w(TAG, "CALL_STATE_CHANGED : updating call state to $newState")
         if ((call.isRinging || newState === CallStatus.CURRENT) && call.timestamp == 0L) {
@@ -526,8 +511,7 @@ class ConversationFacade(
             mNotificationService.handleCallNotification(conference, false)
             mHardwareService.setPreviewSettings()
         } else if (newState === CallStatus.CURRENT && call.isIncoming
-            || newState === CallStatus.RINGING && !call.isIncoming
-        ) {
+            || newState === CallStatus.RINGING && !call.isIncoming) {
             mNotificationService.handleCallNotification(conference, false)
             mAccountService.sendProfile(call.daemonIdString!!, call.account!!)
         } else if (newState === CallStatus.HUNGUP || newState === CallStatus.BUSY || newState === CallStatus.FAILURE || newState === CallStatus.OVER) {
@@ -655,12 +639,12 @@ class ConversationFacade(
             .subscribe())
         mDisposableBag.add(mAccountService.observableAccountList
             .switchMap { accounts: List<Account> ->
-                val r: MutableList<Observable<Tuple<Account, ContactLocationEntry>>> = ArrayList(accounts.size)
-                for (a in accounts) r.add(a.locationUpdates.map { s: ContactLocationEntry -> Tuple(a, s) })
+                val r: MutableList<Observable<Pair<Account, ContactLocationEntry>>> = ArrayList(accounts.size)
+                for (a in accounts) r.add(a.locationUpdates.map { s: ContactLocationEntry -> Pair(a, s) })
                 Observable.merge(r)
             }
             .distinctUntilChanged()
-            .subscribe { t: Tuple<Account, ContactLocationEntry> ->
+            .subscribe { t: Pair<Account, ContactLocationEntry> ->
                 Log.e(TAG, "Location reception started for " + t.second.contact)
                 mNotificationService.showLocationNotification(t.first, t.second.contact)
                 mDisposableBag.add(t.second.location.doOnComplete {
diff --git a/ring-android/libringclient/src/main/java/net/jami/services/DaemonService.java b/ring-android/libringclient/src/main/java/net/jami/services/DaemonService.java
deleted file mode 100644
index d947844bf..000000000
--- a/ring-android/libringclient/src/main/java/net/jami/services/DaemonService.java
+++ /dev/null
@@ -1,423 +0,0 @@
-/*
- *  Copyright (C) 2004-2021 Savoir-faire Linux Inc.
- *
- *  Author: Thibault Wittemberg <thibault.wittemberg@savoirfairelinux.com>
- *  Author: Adrien Béraud <adrien.beraud@savoirfairelinux.com>
- *
- *  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 <https://www.gnu.org/licenses/>.
- */
-package net.jami.services;
-
-import net.jami.daemon.Blob;
-import net.jami.daemon.Callback;
-import net.jami.daemon.ConfigurationCallback;
-import net.jami.daemon.ConversationCallback;
-import net.jami.daemon.DataTransferCallback;
-import net.jami.daemon.IntVect;
-import net.jami.daemon.IntegerMap;
-import net.jami.daemon.JamiService;
-import net.jami.daemon.PresenceCallback;
-import net.jami.daemon.StringMap;
-import net.jami.daemon.StringVect;
-import net.jami.daemon.UintVect;
-import net.jami.daemon.VectMap;
-import net.jami.daemon.VideoCallback;
-import net.jami.model.Uri;
-import net.jami.utils.Log;
-
-import java.util.Map;
-import java.util.concurrent.ScheduledExecutorService;
-
-import javax.inject.Inject;
-import javax.inject.Named;
-
-public class DaemonService {
-
-    private static final String TAG = DaemonService.class.getSimpleName();
-
-    @Named("DaemonExecutor")
-    private final ScheduledExecutorService mExecutor;
-    private final CallService mCallService;
-    private final HardwareService mHardwareService;
-    private final AccountService mAccountService;
-    private final SystemInfoCallbacks mSystemInfoCallbacks;
-
-    // references must be kept to avoid garbage collection while pointers are stored in the daemon.
-    private DaemonVideoCallback mHardwareCallback;
-    private DaemonPresenceCallback mPresenceCallback;
-    private DaemonCallAndConferenceCallback mCallAndConferenceCallback;
-    private DaemonConfigurationCallback mConfigurationCallback;
-    private DaemonDataTransferCallback mDataCallback;
-    private ConversationCallback mConversationCallback;
-
-    private boolean mDaemonStarted = false;
-
-    public DaemonService(SystemInfoCallbacks systemInfoCallbacks, ScheduledExecutorService executor, CallService callService, HardwareService hardwareService, AccountService accountService) {
-        mSystemInfoCallbacks = systemInfoCallbacks;
-        mExecutor = executor;
-        mCallService = callService;
-        mHardwareService = hardwareService;
-        mAccountService = accountService;
-    }
-
-    public interface SystemInfoCallbacks {
-        void getHardwareAudioFormat(IntVect ret);
-
-        void getAppDataPath(String name, StringVect ret);
-
-        void getDeviceName(StringVect ret);
-    }
-
-    public boolean isStarted() {
-        return mDaemonStarted;
-    }
-
-    synchronized public void startDaemon() {
-        if (!mDaemonStarted) {
-            mDaemonStarted = true;
-            Log.i(TAG, "Starting daemon ...");
-            mHardwareCallback = new DaemonVideoCallback();
-            mPresenceCallback = new DaemonPresenceCallback();
-            mCallAndConferenceCallback = new DaemonCallAndConferenceCallback();
-            mConfigurationCallback = new DaemonConfigurationCallback();
-            mDataCallback = new DaemonDataTransferCallback();
-            mConversationCallback = new ConversationCallbackImpl();
-            JamiService.init(mConfigurationCallback, mCallAndConferenceCallback, mPresenceCallback, mDataCallback, mHardwareCallback, mConversationCallback);
-            Log.i(TAG, "DaemonService started");
-        }
-    }
-
-    synchronized public void stopDaemon() {
-        mExecutor.shutdown();
-        if (mDaemonStarted) {
-            Log.i(TAG, "stopping daemon ...");
-            JamiService.fini();
-            mDaemonStarted = false;
-            Log.i(TAG, "DaemonService stopped");
-        }
-    }
-
-    class DaemonConfigurationCallback extends ConfigurationCallback {
-
-        @Override
-        public void volumeChanged(String device, int value) {
-            mAccountService.volumeChanged(device, value);
-        }
-
-        @Override
-        public void accountsChanged() {
-            mExecutor.submit(() -> mAccountService.accountsChanged());
-        }
-
-        @Override
-        public void stunStatusFailure(String accountId) {
-            mAccountService.stunStatusFailure(accountId);
-        }
-
-        @Override
-        public void registrationStateChanged(String accountId, String newState, int code, String detailString) {
-            mExecutor.submit(() -> mAccountService.registrationStateChanged(accountId, newState, code, detailString));
-        }
-
-        @Override
-        public void volatileAccountDetailsChanged(String account_id, StringMap details) {
-            Map<String, String> jdetails = details.toNative();
-            mExecutor.submit(() -> mAccountService.volatileAccountDetailsChanged(account_id, jdetails));
-        }
-
-        @Override
-        public void accountDetailsChanged(String account_id, StringMap details) {
-            Map<String, String> jdetails = details.toNative();
-            mExecutor.submit(() -> mAccountService.accountDetailsChanged(account_id, jdetails));
-        }
-
-        @Override
-        public void profileReceived(String accountId, String peerId, String path) {
-            mExecutor.submit(() -> mAccountService.profileReceived(accountId, peerId, path));
-        }
-
-        @Override
-        public void accountProfileReceived(String account_id, String name, String photo) {
-            mAccountService.accountProfileReceived(account_id, name, photo);
-        }
-
-        @Override
-        public void incomingAccountMessage(String accountId, String from, String messageId, StringMap messages) {
-            if (messages == null || messages.isEmpty())
-                return;
-            Map<String, String> jmessages = messages.toNativeFromUtf8();
-            mExecutor.submit(() -> mAccountService.incomingAccountMessage(accountId, messageId, null, from, jmessages));
-        }
-
-        @Override
-        public void accountMessageStatusChanged(String accountId, String conversationId, String peer, String messageId, int status) {
-            mExecutor.submit(() -> mAccountService.accountMessageStatusChanged(accountId, conversationId, messageId, peer, status));
-        }
-
-        @Override
-        public void composingStatusChanged(String accountId, String conversationId, String contactUri, int status) {
-            mExecutor.submit(() -> mAccountService.composingStatusChanged(accountId, conversationId, contactUri, status));
-        }
-
-        @Override
-        public void errorAlert(int alert) {
-            mExecutor.submit(() -> mAccountService.errorAlert(alert));
-        }
-
-        @Override
-        public void getHardwareAudioFormat(IntVect ret) {
-            mSystemInfoCallbacks.getHardwareAudioFormat(ret);
-        }
-
-        @Override
-        public void getAppDataPath(String name, StringVect ret) {
-            mSystemInfoCallbacks.getAppDataPath(name, ret);
-        }
-
-        @Override
-        public void getDeviceName(StringVect ret) {
-            mSystemInfoCallbacks.getDeviceName(ret);
-        }
-
-        @Override
-        public void knownDevicesChanged(String accountId, StringMap devices) {
-            Map<String, String> jdevices = devices.toNativeFromUtf8();
-            mExecutor.submit(() -> mAccountService.knownDevicesChanged(accountId, jdevices));
-        }
-
-        @Override
-        public void exportOnRingEnded(String accountId, int code, String pin) {
-            mAccountService.exportOnRingEnded(accountId, code, pin);
-        }
-
-        @Override
-        public void nameRegistrationEnded(String accountId, int state, String name) {
-            mAccountService.nameRegistrationEnded(accountId, state, name);
-        }
-
-        @Override
-        public void registeredNameFound(String accountId, int state, String address, String name) {
-            mAccountService.registeredNameFound(accountId, state, address, name);
-        }
-
-        @Override
-        public void userSearchEnded(String accountId, int state, String query, VectMap results) {
-            mAccountService.userSearchEnded(accountId, state, query, results.toNative());
-        }
-
-        @Override
-        public void migrationEnded(String accountId, String state) {
-            mAccountService.migrationEnded(accountId, state);
-        }
-
-        @Override
-        public void deviceRevocationEnded(String accountId, String device, int state) {
-            mAccountService.deviceRevocationEnded(accountId, device, state);
-        }
-
-        @Override
-        public void incomingTrustRequest(String accountId, String conversationId, String from, Blob message, long received) {
-            String jmessage = message.toJavaString();
-            mExecutor.submit(() -> mAccountService.incomingTrustRequest(accountId, conversationId, from, jmessage, received));
-        }
-
-        @Override
-        public void contactAdded(String accountId, String uri, boolean confirmed) {
-            mExecutor.submit(() -> mAccountService.contactAdded(accountId, uri, confirmed));
-        }
-
-        @Override
-        public void contactRemoved(String accountId, String uri, boolean banned) {
-            mExecutor.submit(() -> mAccountService.contactRemoved(accountId, uri, banned));
-        }
-
-        @Override
-        public void messageSend(String message) {
-            mHardwareService.logMessage(message);
-        }
-    }
-
-    class DaemonCallAndConferenceCallback extends Callback {
-
-        @Override
-        public void callStateChanged(String callId, String newState, int detailCode) {
-            mCallService.callStateChanged(callId, newState, detailCode);
-        }
-
-        @Override
-        public void incomingCall(String accountId, String callId, String from) {
-            mCallService.incomingCall(accountId, callId, from);
-        }
-
-        @Override
-        public void connectionUpdate(String id, int state) {
-            mCallService.connectionUpdate(id, state);
-        }
-
-        @Override
-        public void remoteRecordingChanged(String call_id, String peer_number, boolean state) {
-            mCallService.remoteRecordingChanged(call_id, Uri.fromString(peer_number), state);
-        }
-
-        @Override
-        public void onConferenceInfosUpdated(String confId, VectMap infos) {
-            mCallService.onConferenceInfoUpdated(confId, infos.toNative());
-        }
-
-        @Override
-        public void incomingMessage(String callId, String from, StringMap messages) {
-            if (messages == null || messages.isEmpty())
-                return;
-            Map<String, String> jmessages = messages.toNativeFromUtf8();
-            mExecutor.submit(() -> mCallService.incomingMessage(callId, from, jmessages));
-        }
-
-        @Override
-        public void conferenceCreated(final String confId) {
-            mCallService.conferenceCreated(confId);
-        }
-
-        @Override
-        public void conferenceRemoved(String confId) {
-            mCallService.conferenceRemoved(confId);
-        }
-
-        @Override
-        public void conferenceChanged(String confId, String state) {
-            mCallService.conferenceChanged(confId, state);
-        }
-
-        @Override
-        public void recordPlaybackFilepath(String id, String filename) {
-            mCallService.recordPlaybackFilepath(id, filename);
-        }
-
-        @Override
-        public void onRtcpReportReceived(String callId, IntegerMap stats) {
-            mCallService.onRtcpReportReceived(callId);
-        }
-    }
-
-    class DaemonPresenceCallback extends PresenceCallback {
-
-        @Override
-        public void newServerSubscriptionRequest(String remote) {
-            Log.d(TAG, "newServerSubscriptionRequest: " + remote);
-        }
-
-        @Override
-        public void serverError(String accountId, String error, String message) {
-            Log.d(TAG, "serverError: " + accountId + ", " + error + ", " + message);
-        }
-
-        @Override
-        public void newBuddyNotification(String accountId, String buddyUri, int status, String lineStatus) {
-            mAccountService.getAccount(accountId).presenceUpdate(buddyUri, status == 1);
-        }
-
-        @Override
-        public void subscriptionStateChanged(String accountId, String buddyUri, int state) {
-            Log.d(TAG, "subscriptionStateChanged: " + accountId + ", " + buddyUri + ", " + state);
-        }
-    }
-
-    private class DaemonVideoCallback extends VideoCallback {
-
-        @Override
-        public void decodingStarted(String id, String shmPath, int width, int height, boolean isMixer) {
-            mHardwareService.decodingStarted(id, shmPath, width, height, isMixer);
-        }
-
-        @Override
-        public void decodingStopped(String id, String shmPath, boolean isMixer) {
-            mHardwareService.decodingStopped(id, shmPath, isMixer);
-        }
-
-        @Override
-        public void getCameraInfo(String camId, IntVect formats, UintVect sizes, UintVect rates) {
-            mHardwareService.getCameraInfo(camId, formats, sizes, rates);
-        }
-
-        @Override
-        public void setParameters(String camId, int format, int width, int height, int rate) {
-            mHardwareService.setParameters(camId, format, width, height, rate);
-        }
-
-        @Override
-        public void requestKeyFrame() {
-            mHardwareService.requestKeyFrame();
-        }
-
-        @Override
-        public void setBitrate(String device, int bitrate) {
-            mHardwareService.setBitrate(device, bitrate);
-        }
-
-        @Override
-        public void startCapture(String camId) {
-            Log.d(TAG, "startCapture: " + camId);
-            mHardwareService.startCapture(camId);
-        }
-
-        @Override
-        public void stopCapture() {
-            mHardwareService.stopCapture();
-        }
-    }
-
-    class DaemonDataTransferCallback extends DataTransferCallback {
-        @Override
-        public void dataTransferEvent(String accountId, String conversationId, String interactionId, String fileId, int eventCode) {
-            Log.d(TAG, "dataTransferEvent: conversationId=" + conversationId + ", fileId=" + fileId + ", eventCode=" + eventCode);
-            mAccountService.dataTransferEvent(accountId, conversationId, interactionId, fileId, eventCode);
-        }
-    }
-
-    class ConversationCallbackImpl extends ConversationCallback {
-        @Override
-        public void conversationLoaded(long id, String accountId, String conversationId, VectMap messages) {
-            mAccountService.conversationLoaded(accountId, conversationId, messages.toNative());
-        }
-
-        @Override
-        public void conversationReady(String accountId, String conversationId) {
-            mAccountService.conversationReady(accountId, conversationId);
-        }
-
-        @Override
-        public void conversationRemoved(String accountId, String conversationId) {
-            mAccountService.conversationRemoved(accountId, conversationId);
-        }
-
-        @Override
-        public void conversationRequestReceived(String accountId, String conversationId, StringMap metadata) {
-            mAccountService.conversationRequestReceived(accountId, conversationId, metadata.toNative());
-        }
-
-        @Override
-        public void conversationRequestDeclined(String accountId, String conversationId) {
-            mAccountService.conversationRequestDeclined(accountId, conversationId);
-        }
-        @Override
-        public void conversationMemberEvent(String accountId, String conversationId, String uri, int event) {
-            mAccountService.conversationMemberEvent(accountId, conversationId, uri, event);
-        }
-
-        @Override
-        public void messageReceived(String accountId, String conversationId, StringMap message) {
-            mAccountService.messageReceived(accountId, conversationId, message.toNative());
-        }
-    }
-
-}
diff --git a/ring-android/libringclient/src/main/java/net/jami/services/DaemonService.kt b/ring-android/libringclient/src/main/java/net/jami/services/DaemonService.kt
new file mode 100644
index 000000000..7ff7f380c
--- /dev/null
+++ b/ring-android/libringclient/src/main/java/net/jami/services/DaemonService.kt
@@ -0,0 +1,342 @@
+/*
+ *  Copyright (C) 2004-2021 Savoir-faire Linux Inc.
+ *
+ *  Author: Thibault Wittemberg <thibault.wittemberg@savoirfairelinux.com>
+ *  Author: Adrien Béraud <adrien.beraud@savoirfairelinux.com>
+ *
+ *  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 <https://www.gnu.org/licenses/>.
+ */
+package net.jami.services
+
+import net.jami.daemon.*
+import net.jami.model.Uri
+import net.jami.utils.Log
+import java.util.concurrent.ScheduledExecutorService
+import javax.inject.Named
+
+class DaemonService(
+    private val mSystemInfoCallbacks: SystemInfoCallbacks,
+    @Named("DaemonExecutor") private val mExecutor: ScheduledExecutorService,
+    private val mCallService: CallService,
+    private val mHardwareService: HardwareService,
+    private val mAccountService: AccountService
+) {
+    // references must be kept to avoid garbage collection while pointers are stored in the daemon.
+    private var mHardwareCallback: DaemonVideoCallback? = null
+    private var mPresenceCallback: DaemonPresenceCallback? = null
+    private var mCallAndConferenceCallback: DaemonCallAndConferenceCallback? = null
+    private var mConfigurationCallback: DaemonConfigurationCallback? = null
+    private var mDataCallback: DaemonDataTransferCallback? = null
+    private var mConversationCallback: ConversationCallback? = null
+    var isStarted = false
+        private set
+
+    interface SystemInfoCallbacks {
+        fun getHardwareAudioFormat(ret: IntVect)
+        fun getAppDataPath(name: String, ret: StringVect)
+        fun getDeviceName(ret: StringVect)
+    }
+
+    @Synchronized
+    fun startDaemon() {
+        if (!isStarted) {
+            isStarted = true
+            Log.i(TAG, "Starting daemon ...")
+            mHardwareCallback = DaemonVideoCallback()
+            mPresenceCallback = DaemonPresenceCallback()
+            mCallAndConferenceCallback = DaemonCallAndConferenceCallback()
+            mConfigurationCallback = DaemonConfigurationCallback()
+            mDataCallback = DaemonDataTransferCallback()
+            mConversationCallback = ConversationCallbackImpl()
+            JamiService.init(
+                mConfigurationCallback,
+                mCallAndConferenceCallback,
+                mPresenceCallback,
+                mDataCallback,
+                mHardwareCallback,
+                mConversationCallback
+            )
+            Log.i(TAG, "DaemonService started")
+        }
+    }
+
+    @Synchronized
+    fun stopDaemon() {
+        mExecutor.shutdown()
+        if (isStarted) {
+            Log.i(TAG, "stopping daemon ...")
+            JamiService.fini()
+            isStarted = false
+            Log.i(TAG, "DaemonService stopped")
+        }
+    }
+
+    internal inner class DaemonConfigurationCallback : ConfigurationCallback() {
+        override fun volumeChanged(device: String, value: Int) {
+            mAccountService.volumeChanged(device, value)
+        }
+
+        override fun accountsChanged() {
+            mExecutor.submit { mAccountService.accountsChanged() }
+        }
+
+        override fun stunStatusFailure(accountId: String) {
+            mAccountService.stunStatusFailure(accountId)
+        }
+
+        override fun registrationStateChanged(accountId: String, newState: String, code: Int, detailString: String) {
+            mExecutor.submit { mAccountService.registrationStateChanged(accountId, newState, code, detailString) }
+        }
+
+        override fun volatileAccountDetailsChanged(account_id: String, details: StringMap) {
+            val jdetails: Map<String, String> = details.toNative()
+            mExecutor.submit { mAccountService.volatileAccountDetailsChanged(account_id, jdetails) }
+        }
+
+        override fun accountDetailsChanged(account_id: String, details: StringMap) {
+            val jdetails: Map<String, String> = details.toNative()
+            mExecutor.submit { mAccountService.accountDetailsChanged(account_id, jdetails) }
+        }
+
+        override fun profileReceived(accountId: String, peerId: String, path: String) {
+            mExecutor.submit { mAccountService.profileReceived(accountId, peerId, path) }
+        }
+
+        override fun accountProfileReceived(account_id: String, name: String, photo: String) {
+            mAccountService.accountProfileReceived(account_id, name, photo)
+        }
+
+        override fun incomingAccountMessage(accountId: String, from: String, messageId: String, messages: StringMap) {
+            if (messages.isEmpty()) return
+            val jmessages: Map<String, String> = messages.toNativeFromUtf8()
+            mExecutor.submit { mAccountService.incomingAccountMessage(accountId, messageId, null, from, jmessages) }
+        }
+
+        override fun accountMessageStatusChanged(accountId: String, conversationId: String, peer: String, messageId: String, status: Int) {
+            mExecutor.submit {
+                mAccountService.accountMessageStatusChanged(accountId, conversationId, messageId, peer, status)
+            }
+        }
+
+        override fun composingStatusChanged(accountId: String, conversationId: String, contactUri: String, status: Int) {
+            mExecutor.submit { mAccountService.composingStatusChanged(accountId, conversationId, contactUri, status) }
+        }
+
+        override fun errorAlert(alert: Int) {
+            mExecutor.submit { mAccountService.errorAlert(alert) }
+        }
+
+        override fun getHardwareAudioFormat(ret: IntVect) {
+            mSystemInfoCallbacks.getHardwareAudioFormat(ret)
+        }
+
+        override fun getAppDataPath(name: String, ret: StringVect) {
+            mSystemInfoCallbacks.getAppDataPath(name, ret)
+        }
+
+        override fun getDeviceName(ret: StringVect) {
+            mSystemInfoCallbacks.getDeviceName(ret)
+        }
+
+        override fun knownDevicesChanged(accountId: String, devices: StringMap) {
+            val jdevices: Map<String, String> = devices.toNativeFromUtf8()
+            mExecutor.submit { mAccountService.knownDevicesChanged(accountId, jdevices) }
+        }
+
+        override fun exportOnRingEnded(accountId: String, code: Int, pin: String) {
+            mAccountService.exportOnRingEnded(accountId, code, pin)
+        }
+
+        override fun nameRegistrationEnded(accountId: String, state: Int, name: String) {
+            mAccountService.nameRegistrationEnded(accountId, state, name)
+        }
+
+        override fun registeredNameFound(accountId: String, state: Int, address: String, name: String) {
+            mAccountService.registeredNameFound(accountId, state, address, name)
+        }
+
+        override fun userSearchEnded(accountId: String, state: Int, query: String, results: VectMap) {
+            mAccountService.userSearchEnded(accountId, state, query, results.toNative())
+        }
+
+        override fun migrationEnded(accountId: String, state: String) {
+            mAccountService.migrationEnded(accountId, state)
+        }
+
+        override fun deviceRevocationEnded(accountId: String, device: String, state: Int) {
+            mAccountService.deviceRevocationEnded(accountId, device, state)
+        }
+
+        override fun incomingTrustRequest(accountId: String, conversationId: String, from: String, message: Blob, received: Long) {
+            val jmessage = message.toJavaString()
+            mExecutor.submit {
+                mAccountService.incomingTrustRequest(accountId, conversationId, from, jmessage, received)
+            }
+        }
+
+        override fun contactAdded(accountId: String, uri: String, confirmed: Boolean) {
+            mExecutor.submit { mAccountService.contactAdded(accountId, uri, confirmed) }
+        }
+
+        override fun contactRemoved(accountId: String, uri: String, banned: Boolean) {
+            mExecutor.submit { mAccountService.contactRemoved(accountId, uri, banned) }
+        }
+
+        override fun messageSend(message: String) {
+            mHardwareService.logMessage(message)
+        }
+    }
+
+    internal inner class DaemonCallAndConferenceCallback : Callback() {
+        override fun callStateChanged(callId: String, newState: String, detailCode: Int) {
+            mCallService.callStateChanged(callId, newState, detailCode)
+        }
+
+        override fun incomingCall(accountId: String, callId: String, from: String) {
+            mCallService.incomingCall(accountId, callId, from)
+        }
+
+        override fun connectionUpdate(id: String, state: Int) {
+            mCallService.connectionUpdate(id, state)
+        }
+
+        override fun remoteRecordingChanged(call_id: String, peer_number: String, state: Boolean) {
+            mCallService.remoteRecordingChanged(call_id, Uri.fromString(peer_number), state)
+        }
+
+        override fun onConferenceInfosUpdated(confId: String, infos: VectMap) {
+            mCallService.onConferenceInfoUpdated(confId, infos.toNative())
+        }
+
+        override fun incomingMessage(callId: String, from: String, messages: StringMap) {
+            if (messages.isEmpty()) return
+            val jmessages: Map<String, String> = messages.toNativeFromUtf8()
+            mExecutor.submit { mCallService.incomingMessage(callId, from, jmessages) }
+        }
+
+        override fun conferenceCreated(confId: String) {
+            mCallService.conferenceCreated(confId)
+        }
+
+        override fun conferenceRemoved(confId: String) {
+            mCallService.conferenceRemoved(confId)
+        }
+
+        override fun conferenceChanged(confId: String, state: String) {
+            mCallService.conferenceChanged(confId, state)
+        }
+
+        override fun recordPlaybackFilepath(id: String, filename: String) {
+            mCallService.recordPlaybackFilepath(id, filename)
+        }
+
+        override fun onRtcpReportReceived(callId: String, stats: IntegerMap) {
+            mCallService.onRtcpReportReceived(callId)
+        }
+    }
+
+    internal inner class DaemonPresenceCallback : PresenceCallback() {
+        override fun newServerSubscriptionRequest(remote: String) {
+            Log.d(TAG, "newServerSubscriptionRequest: $remote")
+        }
+
+        override fun serverError(accountId: String, error: String, message: String) {
+            Log.d(TAG, "serverError: $accountId, $error, $message")
+        }
+
+        override fun newBuddyNotification(accountId: String, buddyUri: String, status: Int, lineStatus: String) {
+            mAccountService.getAccount(accountId)!!.presenceUpdate(buddyUri, status == 1)
+        }
+
+        override fun subscriptionStateChanged(accountId: String, buddyUri: String, state: Int) {
+            Log.d(TAG, "subscriptionStateChanged: $accountId, $buddyUri, $state")
+        }
+    }
+
+    private inner class DaemonVideoCallback : VideoCallback() {
+        override fun decodingStarted(id: String, shmPath: String, width: Int, height: Int, isMixer: Boolean) {
+            mHardwareService.decodingStarted(id, shmPath, width, height, isMixer)
+        }
+
+        override fun decodingStopped(id: String, shmPath: String, isMixer: Boolean) {
+            mHardwareService.decodingStopped(id, shmPath, isMixer)
+        }
+
+        override fun getCameraInfo(camId: String, formats: IntVect, sizes: UintVect, rates: UintVect) {
+            mHardwareService.getCameraInfo(camId, formats, sizes, rates)
+        }
+
+        override fun setParameters(camId: String, format: Int, width: Int, height: Int, rate: Int) {
+            mHardwareService.setParameters(camId, format, width, height, rate)
+        }
+
+        override fun requestKeyFrame() {
+            mHardwareService.requestKeyFrame()
+        }
+
+        override fun setBitrate(device: String, bitrate: Int) {
+            mHardwareService.setBitrate(device, bitrate)
+        }
+
+        override fun startCapture(camId: String) {
+            Log.d(TAG, "startCapture: $camId")
+            mHardwareService.startCapture(camId)
+        }
+
+        override fun stopCapture() {
+            mHardwareService.stopCapture()
+        }
+    }
+
+    internal inner class DaemonDataTransferCallback : DataTransferCallback() {
+        override fun dataTransferEvent(accountId: String, conversationId: String, interactionId: String, fileId: String, eventCode: Int) {
+            Log.d(TAG, "dataTransferEvent: conversationId=$conversationId, fileId=$fileId, eventCode=$eventCode")
+            mAccountService.dataTransferEvent(accountId, conversationId, interactionId, fileId, eventCode)
+        }
+    }
+
+    internal inner class ConversationCallbackImpl : ConversationCallback() {
+        override fun conversationLoaded(id: Long, accountId: String, conversationId: String, messages: VectMap) {
+            mAccountService.conversationLoaded(accountId, conversationId, messages.toNative())
+        }
+
+        override fun conversationReady(accountId: String, conversationId: String) {
+            mAccountService.conversationReady(accountId, conversationId)
+        }
+
+        override fun conversationRemoved(accountId: String, conversationId: String) {
+            mAccountService.conversationRemoved(accountId, conversationId)
+        }
+
+        override fun conversationRequestReceived(accountId: String, conversationId: String, metadata: StringMap) {
+            mAccountService.conversationRequestReceived(accountId, conversationId, metadata.toNative())
+        }
+
+        override fun conversationRequestDeclined(accountId: String, conversationId: String) {
+            mAccountService.conversationRequestDeclined(accountId, conversationId)
+        }
+
+        override fun conversationMemberEvent(accountId: String, conversationId: String, uri: String, event: Int) {
+            mAccountService.conversationMemberEvent(accountId, conversationId, uri, event)
+        }
+
+        override fun messageReceived(accountId: String, conversationId: String, message: StringMap) {
+            mAccountService.messageReceived(accountId, conversationId, message.toNative())
+        }
+    }
+
+    companion object {
+        private val TAG = DaemonService::class.simpleName!!
+    }
+}
\ No newline at end of file
diff --git a/ring-android/libringclient/src/main/java/net/jami/services/DeviceRuntimeService.kt b/ring-android/libringclient/src/main/java/net/jami/services/DeviceRuntimeService.kt
index 38ab0eb9c..2c6da03b8 100644
--- a/ring-android/libringclient/src/main/java/net/jami/services/DeviceRuntimeService.kt
+++ b/ring-android/libringclient/src/main/java/net/jami/services/DeviceRuntimeService.kt
@@ -31,10 +31,9 @@ abstract class DeviceRuntimeService : SystemInfoCallbacks {
     abstract fun getConversationPath(conversationId: String, name: String): File
     abstract fun getConversationPath(accountId: String, conversationId: String, name: String): File
     fun getConversationPath(interaction: DataTransfer): File {
-        return if (interaction.conversationId == null) getConversationPath(
-            interaction.conversation!!.participant,
-            interaction.storagePath
-        ) else interaction.publicPath!!
+        return if (interaction.conversationId == null)
+            getConversationPath(interaction.conversation!!.participant!!, interaction.storagePath)
+        else interaction.publicPath!!
     }
 
     fun getNewConversationPath(accountId: String, conversationId: String, name: String): File {
diff --git a/ring-android/libringclient/src/main/java/net/jami/services/HardwareService.kt b/ring-android/libringclient/src/main/java/net/jami/services/HardwareService.kt
index 91239b566..7345ec96f 100644
--- a/ring-android/libringclient/src/main/java/net/jami/services/HardwareService.kt
+++ b/ring-android/libringclient/src/main/java/net/jami/services/HardwareService.kt
@@ -26,7 +26,6 @@ import io.reactivex.rxjava3.schedulers.Schedulers
 import io.reactivex.rxjava3.subjects.BehaviorSubject
 import io.reactivex.rxjava3.subjects.PublishSubject
 import io.reactivex.rxjava3.subjects.Subject
-import net.jami.utils.StringUtils.isEmpty
 import net.jami.model.Call.CallStatus
 import net.jami.daemon.IntVect
 import net.jami.daemon.UintVect
@@ -53,33 +52,19 @@ abstract class HardwareService(
         var callId: String? = null
     }
 
-    class BluetoothEvent {
-        var connected = false
-    }
+    class BluetoothEvent (val connected: Boolean)
 
     enum class AudioOutput {
         INTERNAL, SPEAKERS, BLUETOOTH
     }
 
-    class AudioState {
-        val outputType: AudioOutput
-        val outputName: String?
-
-        constructor(ot: AudioOutput) {
-            outputType = ot
-            outputName = null
-        }
-
-        constructor(ot: AudioOutput, name: String?) {
-            outputType = ot
-            outputName = name
-        }
-    }
+    class AudioState(val outputType: AudioOutput, val outputName: String? = null)
 
     protected val videoEvents: Subject<VideoEvent> = PublishSubject.create()
     protected val bluetoothEvents: Subject<BluetoothEvent> = PublishSubject.create()
     protected val audioStateSubject: Subject<AudioState> = BehaviorSubject.createDefault(STATE_INTERNAL)
     protected val connectivityEvents: Subject<Boolean> = BehaviorSubject.create()
+
     fun getVideoEvents(): Observable<VideoEvent> {
         return videoEvents
     }
@@ -114,18 +99,18 @@ abstract class HardwareService(
     abstract fun endCapture()
     abstract fun stopScreenShare()
     abstract fun requestKeyFrame()
-    abstract fun setBitrate(device: String?, bitrate: Int)
-    abstract fun addVideoSurface(id: String?, holder: Any?)
-    abstract fun updateVideoSurfaceId(currentId: String?, newId: String?)
-    abstract fun removeVideoSurface(id: String?)
-    abstract fun addPreviewVideoSurface(holder: Any?, conference: Conference?)
-    abstract fun updatePreviewVideoSurface(conference: Conference?)
+    abstract fun setBitrate(device: String, bitrate: Int)
+    abstract fun addVideoSurface(id: String, holder: Any)
+    abstract fun updateVideoSurfaceId(currentId: String, newId: String)
+    abstract fun removeVideoSurface(id: String)
+    abstract fun addPreviewVideoSurface(holder: Any, conference: Conference?)
+    abstract fun updatePreviewVideoSurface(conference: Conference)
     abstract fun removePreviewVideoSurface()
-    abstract fun switchInput(id: String?, setDefaultCamera: Boolean)
+    abstract fun switchInput(id: String, setDefaultCamera: Boolean)
     abstract fun setPreviewSettings()
     abstract fun hasCamera(): Boolean
     abstract val cameraCount: Int
-    abstract val maxResolutions: Observable<Tuple<Int, Int>>
+    abstract val maxResolutions: Observable<Tuple<Int?, Int?>>
     abstract val isPreviewFromFrontCamera: Boolean
     abstract fun shouldPlaySpeaker(): Boolean
     abstract fun unregisterCameraDetectionCallback()
@@ -137,12 +122,12 @@ abstract class HardwareService(
         mExecutor.execute { JamiService.connectivityChanged() }
     }
 
-    protected fun switchInput(id: String?, uri: String) {
+    protected fun switchInput(id: String, uri: String) {
         Log.i(TAG, "switchInput() $uri")
         mExecutor.execute { JamiService.switchInput(id, uri) }
     }
 
-    fun setPreviewSettings(cameraMaps: Map<String?, StringMap?>) {
+    fun setPreviewSettings(cameraMaps: Map<String, StringMap>) {
         mExecutor.execute {
             Log.i(TAG, "applySettings() thread running...")
             for ((key, value) in cameraMaps) {
@@ -151,7 +136,7 @@ abstract class HardwareService(
         }
     }
 
-    fun startVideo(inputId: String?, surface: Any?, width: Int, height: Int): Long {
+    fun startVideo(inputId: String, surface: Any, width: Int, height: Int): Long {
         val inputWindow = JamiService.acquireNativeWindow(surface)
         if (inputWindow == 0L) {
             return inputWindow
@@ -161,7 +146,7 @@ abstract class HardwareService(
         return inputWindow
     }
 
-    fun stopVideo(inputId: String?, inputWindow: Long) {
+    fun stopVideo(inputId: String, inputWindow: Long) {
         if (inputWindow == 0L) {
             return
         }
@@ -170,7 +155,7 @@ abstract class HardwareService(
     }
 
     abstract fun setDeviceOrientation(rotation: Int)
-    protected abstract val videoDevices: List<String?>?
+    protected abstract val videoDevices: List<String>
     private var logs: Observable<String>? = null
     private var logEmitter: Emitter<String>? = null
 
diff --git a/ring-android/libringclient/src/main/java/net/jami/services/HistoryService.kt b/ring-android/libringclient/src/main/java/net/jami/services/HistoryService.kt
index 99a33285d..d207768b9 100644
--- a/ring-android/libringclient/src/main/java/net/jami/services/HistoryService.kt
+++ b/ring-android/libringclient/src/main/java/net/jami/services/HistoryService.kt
@@ -117,12 +117,12 @@ abstract class HistoryService {
             Log.d(TAG, "Inserting interaction for account -> $accountId")
             val conversationDataDao = getConversationDataDao(accountId)
             val history = conversationDataDao.queryBuilder().where().eq(ConversationHistory.COLUMN_PARTICIPANT, conversation.participant).queryForFirst() ?:
-            conversationDataDao.createIfNotExists(ConversationHistory(conversation.participant))!!
+            conversationDataDao.createIfNotExists(ConversationHistory(conversation.participant!!))!!
             //interaction.setConversation(conversation);
             conversation.id = history.id
             getInteractionDataDao(accountId).create(interaction)
         }
-            .doOnError { e: Throwable? -> Log.e(TAG, "Can't insert interaction", e) }
+            .doOnError { e: Throwable -> Log.e(TAG, "Can't insert interaction", e) }
             .subscribeOn(scheduler)
     }
 
@@ -177,8 +177,8 @@ abstract class HistoryService {
                     .orderBy(Interaction.COLUMN_TIMESTAMP, true)
                     .where().eq(Interaction.COLUMN_CONVERSATION, conversationId)
                     .prepare())
-        }.subscribeOn(scheduler).doOnError { e: Throwable? -> Log.e(TAG, "Can't load conversation from database", e) }
-            .onErrorReturn { e: Throwable? -> ArrayList() }
+        }.subscribeOn(scheduler).doOnError { e: Throwable -> Log.e(TAG, "Can't load conversation from database", e) }
+            .onErrorReturn { ArrayList() }
     }
 
     fun incomingMessage(accountId: String, daemonId: String?, from: String, message: String): Single<TextMessage> {
diff --git a/ring-android/libringclient/src/main/java/net/jami/services/NotificationService.java b/ring-android/libringclient/src/main/java/net/jami/services/NotificationService.java
deleted file mode 100644
index 7239c8d08..000000000
--- a/ring-android/libringclient/src/main/java/net/jami/services/NotificationService.java
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- *  Copyright (C) 2004-2021 Savoir-faire Linux Inc.
- *
- *  Author: Aline Bonnet <aline.bonnet@savoirfairelinux.com>
- *  Author: Adrien Béraud <adrien.beraud@savoirfairelinux.com>
- *
- *  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, write to the Free Software
- *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-package net.jami.services;
-
-import net.jami.model.Account;
-import net.jami.model.Contact;
-import net.jami.model.Conference;
-import net.jami.model.Conversation;
-import net.jami.model.DataTransfer;
-import net.jami.model.Call;
-import net.jami.model.Uri;
-
-public interface NotificationService {
-    String TRUST_REQUEST_NOTIFICATION_ACCOUNT_ID = "trustRequestNotificationAccountId";
-    String TRUST_REQUEST_NOTIFICATION_FROM = "trustRequestNotificationFrom";
-    String KEY_CALL_ID = "callId";
-    String KEY_HOLD_ID = "holdId";
-    String KEY_END_ID = "endId";
-    String KEY_NOTIFICATION_ID = "notificationId";
-
-    Object showCallNotification(int callId);
-    void cancelCallNotification();
-    void handleCallNotification(Conference conference, boolean remove);
-    void showMissedCallNotification(Call call);
-
-    void showTextNotification(String accountId, Conversation conversation);
-    void cancelTextNotification(String accountId, Uri contact);
-
-    void cancelAll();
-
-    void showIncomingTrustRequestNotification(Account account);
-    void cancelTrustRequestNotification(String accountID);
-
-    void showFileTransferNotification(Conversation conversation, DataTransfer info);
-    void cancelFileNotification(int id, boolean isMigratingToService);
-    void handleDataTransferNotification(DataTransfer transfer, Conversation contact, boolean remove);
-    void removeTransferNotification(String accountId, Uri conversationUri, String fileId);
-    Object getDataTransferNotification(int notificationId);
-
-    //void updateNotification(Object notification, int notificationId);
-
-    Object getServiceNotification();
-
-    void onConnectionUpdate(Boolean b);
-
-    void showLocationNotification(Account first, Contact contact);
-    void cancelLocationNotification(Account first, Contact contact);
-
-}
diff --git a/ring-android/libringclient/src/main/java/net/jami/services/NotificationService.kt b/ring-android/libringclient/src/main/java/net/jami/services/NotificationService.kt
new file mode 100644
index 000000000..1270afbef
--- /dev/null
+++ b/ring-android/libringclient/src/main/java/net/jami/services/NotificationService.kt
@@ -0,0 +1,55 @@
+/*
+ *  Copyright (C) 2004-2021 Savoir-faire Linux Inc.
+ *
+ *  Author: Aline Bonnet <aline.bonnet@savoirfairelinux.com>
+ *  Author: Adrien Béraud <adrien.beraud@savoirfairelinux.com>
+ *
+ *  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, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+package net.jami.services
+
+import net.jami.model.*
+
+interface NotificationService {
+    fun showCallNotification(callId: Int): Any?
+    fun cancelCallNotification()
+    fun handleCallNotification(conference: Conference, remove: Boolean)
+    fun showMissedCallNotification(call: Call)
+    fun showTextNotification(accountId: String, conversation: Conversation)
+    fun cancelTextNotification(accountId: String, contact: Uri)
+    fun cancelAll()
+    fun showIncomingTrustRequestNotification(account: Account)
+    fun cancelTrustRequestNotification(accountID: String)
+    fun showFileTransferNotification(conversation: Conversation, info: DataTransfer)
+    fun cancelFileNotification(id: Int, isMigratingToService: Boolean)
+    fun handleDataTransferNotification(transfer: DataTransfer, contact: Conversation, remove: Boolean)
+    fun removeTransferNotification(accountId: String, conversationUri: Uri, fileId: String)
+    fun getDataTransferNotification(notificationId: Int): Any
+
+    //void updateNotification(Object notification, int notificationId);
+    val serviceNotification: Any
+    fun onConnectionUpdate(b: Boolean)
+    fun showLocationNotification(first: Account, contact: Contact)
+    fun cancelLocationNotification(first: Account, contact: Contact)
+
+    companion object {
+        const val TRUST_REQUEST_NOTIFICATION_ACCOUNT_ID = "trustRequestNotificationAccountId"
+        const val TRUST_REQUEST_NOTIFICATION_FROM = "trustRequestNotificationFrom"
+        const val KEY_CALL_ID = "callId"
+        const val KEY_HOLD_ID = "holdId"
+        const val KEY_END_ID = "endId"
+        const val KEY_NOTIFICATION_ID = "notificationId"
+    }
+}
\ No newline at end of file
diff --git a/ring-android/libringclient/src/main/java/net/jami/services/PreferencesService.java b/ring-android/libringclient/src/main/java/net/jami/services/PreferencesService.java
deleted file mode 100644
index 2062fdf32..000000000
--- a/ring-android/libringclient/src/main/java/net/jami/services/PreferencesService.java
+++ /dev/null
@@ -1,100 +0,0 @@
-/*
- *  Copyright (C) 2004-2021 Savoir-faire Linux Inc.
- *
- *  Author: Thibault Wittemberg <thibault.wittemberg@savoirfairelinux.com>
- *  Author: Adrien Béraud <adrien.beraud@savoirfairelinux.com>
- *
- *  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, write to the Free Software
- *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-package net.jami.services;
-
-import java.util.Set;
-
-import javax.inject.Inject;
-
-import net.jami.model.Settings;
-
-import io.reactivex.rxjava3.core.Observable;
-import io.reactivex.rxjava3.subjects.BehaviorSubject;
-import io.reactivex.rxjava3.subjects.Subject;
-
-public abstract class PreferencesService {
-
-    private final AccountService mAccountService;
-    private final DeviceRuntimeService mDeviceService;
-
-    public PreferencesService(AccountService accountService, DeviceRuntimeService deviceService) {
-        mAccountService = accountService;
-        mDeviceService = deviceService;
-    }
-
-    private Settings mUserSettings;
-    private final Subject<Settings> mSettingsSubject = BehaviorSubject.create();
-
-    protected abstract Settings loadSettings();
-    protected abstract void saveSettings(Settings settings);
-
-    public Settings getSettings() {
-        if (mUserSettings == null) {
-            mUserSettings = loadSettings();
-            mSettingsSubject.onNext(mUserSettings);
-        }
-        return new Settings(mUserSettings);
-    }
-
-    public void setSettings(Settings settings) {
-        saveSettings(settings);
-        boolean allowPush = settings.isAllowPushNotifications();
-        if (mUserSettings == null || mUserSettings.isAllowPushNotifications() != allowPush) {
-            mAccountService.setPushNotificationToken(allowPush ? mDeviceService.getPushToken() : "");
-            mAccountService.setProxyEnabled(allowPush);
-        }
-        mUserSettings = settings;
-        mSettingsSubject.onNext(settings);
-    }
-
-    protected Settings getUserSettings() {
-        return mUserSettings;
-    }
-
-    public Observable<Settings> getSettingsSubject() {
-        return mSettingsSubject;
-    }
-
-    public abstract boolean hasNetworkConnected();
-
-    public abstract boolean isPushAllowed();
-
-    public abstract void saveRequestPreferences(String accountId, String contactId);
-
-    public abstract Set<String> loadRequestsPreferences(String accountId);
-
-    public abstract void removeRequestPreferences(String accountId, String contactId);
-
-    public abstract int getResolution();
-
-    public abstract int getBitrate();
-
-    public abstract boolean isHardwareAccelerationEnabled();
-
-
-    public abstract void setDarkMode(boolean enabled);
-
-    public abstract boolean getDarkMode();
-
-    public abstract void loadDarkMode();
-
-    public abstract int getMaxFileAutoAccept(String accountId);
-}
diff --git a/ring-android/libringclient/src/main/java/net/jami/services/PreferencesService.kt b/ring-android/libringclient/src/main/java/net/jami/services/PreferencesService.kt
new file mode 100644
index 000000000..2bb700694
--- /dev/null
+++ b/ring-android/libringclient/src/main/java/net/jami/services/PreferencesService.kt
@@ -0,0 +1,70 @@
+/*
+ *  Copyright (C) 2004-2021 Savoir-faire Linux Inc.
+ *
+ *  Author: Thibault Wittemberg <thibault.wittemberg@savoirfairelinux.com>
+ *  Author: Adrien Béraud <adrien.beraud@savoirfairelinux.com>
+ *
+ *  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, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+package net.jami.services
+
+import io.reactivex.rxjava3.core.Observable
+import io.reactivex.rxjava3.subjects.BehaviorSubject
+import io.reactivex.rxjava3.subjects.Subject
+import net.jami.model.Settings
+
+abstract class PreferencesService(
+    private val mAccountService: AccountService,
+    private val mDeviceService: DeviceRuntimeService
+) {
+    protected var userSettings: Settings? = null
+        private set
+    private val mSettingsSubject: Subject<Settings> = BehaviorSubject.create()
+    protected abstract fun loadSettings(): Settings
+    protected abstract fun saveSettings(settings: Settings)
+    var settings: Settings
+        get() {
+            if (userSettings == null) {
+                val newSettings = loadSettings()
+                userSettings = newSettings
+                mSettingsSubject.onNext(newSettings)
+            }
+            return Settings(userSettings)
+        }
+        set(settings) {
+            saveSettings(settings)
+            val allowPush = settings.isAllowPushNotifications
+            if (userSettings == null || userSettings!!.isAllowPushNotifications != allowPush) {
+                mAccountService.setPushNotificationToken(if (allowPush) mDeviceService.pushToken else "")
+                mAccountService.setProxyEnabled(allowPush)
+            }
+            userSettings = settings
+            mSettingsSubject.onNext(settings)
+        }
+    val settingsSubject: Observable<Settings>
+        get() = mSettingsSubject
+
+    abstract fun hasNetworkConnected(): Boolean
+    abstract val isPushAllowed: Boolean
+    abstract fun saveRequestPreferences(accountId: String, contactId: String)
+    abstract fun loadRequestsPreferences(accountId: String): Set<String>
+    abstract fun removeRequestPreferences(accountId: String, contactId: String)
+    abstract val resolution: Int
+    abstract val bitrate: Int
+    abstract val isHardwareAccelerationEnabled: Boolean
+    abstract var darkMode: Boolean
+    abstract fun loadDarkMode()
+    abstract fun getMaxFileAutoAccept(accountId: String): Int
+}
\ No newline at end of file
diff --git a/ring-android/libringclient/src/main/java/net/jami/services/VCardService.java b/ring-android/libringclient/src/main/java/net/jami/services/VCardService.java
deleted file mode 100644
index 87631ba8b..000000000
--- a/ring-android/libringclient/src/main/java/net/jami/services/VCardService.java
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- *  Copyright (C) 2004-2021 Savoir-faire Linux Inc.
- *
- *  Author: Hadrien De Sousa <hadrien.desousa@savoirfairelinux.com>
- *
- *  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, write to the Free Software
- *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-package net.jami.services;
-
-import java.io.File;
-
-import net.jami.model.Account;
-import net.jami.utils.Tuple;
-import ezvcard.VCard;
-import io.reactivex.rxjava3.core.Maybe;
-import io.reactivex.rxjava3.core.Observable;
-import io.reactivex.rxjava3.core.Single;
-
-public abstract class VCardService {
-
-    public static final int MAX_SIZE_SIP = 256 * 1024;
-    public static final int MAX_SIZE_REQUEST = 16 * 1024;
-
-    public abstract Observable<Tuple<String, Object>> loadProfile(Account account);
-
-    public abstract Maybe<VCard> loadSmallVCard(String accountId, int maxSize);
-    public Single<VCard> loadSmallVCardWithDefault(String accountId, int maxSize) {
-        return loadSmallVCard(accountId, maxSize)
-                .switchIfEmpty(Single.fromCallable(VCard::new));
-    }
-
-    public abstract Single<VCard> saveVCardProfile(String accountId, String uri, String displayName, String picture);
-    public abstract Single<Tuple<String, Object>> loadVCardProfile(VCard vcard);
-    public abstract Single<Tuple<String, Object>> peerProfileReceived(String accountId, String peerId, File vcard);
-
-    public abstract Object base64ToBitmap(String base64);
-
-}
diff --git a/ring-android/libringclient/src/main/java/net/jami/services/VCardService.kt b/ring-android/libringclient/src/main/java/net/jami/services/VCardService.kt
new file mode 100644
index 000000000..619b5a964
--- /dev/null
+++ b/ring-android/libringclient/src/main/java/net/jami/services/VCardService.kt
@@ -0,0 +1,49 @@
+/*
+ *  Copyright (C) 2004-2021 Savoir-faire Linux Inc.
+ *
+ *  Author: Hadrien De Sousa <hadrien.desousa@savoirfairelinux.com>
+ *
+ *  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, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+package net.jami.services
+
+import ezvcard.VCard
+import io.reactivex.rxjava3.core.Maybe
+import io.reactivex.rxjava3.core.Observable
+import io.reactivex.rxjava3.core.Single
+import net.jami.model.Account
+import net.jami.model.Profile
+import java.io.File
+
+abstract class VCardService {
+    abstract fun loadProfile(account: Account): Observable<Profile>
+    abstract fun loadSmallVCard(accountId: String, maxSize: Int): Maybe<VCard>
+    fun loadSmallVCardWithDefault(accountId: String, maxSize: Int): Single<VCard> {
+        return loadSmallVCard(accountId, maxSize)
+            .switchIfEmpty(Single.fromCallable { VCard() })
+    }
+
+    abstract fun saveVCardProfile(accountId: String, uri: String?, displayName: String?, picture: String?): Single<VCard>
+
+    abstract fun loadVCardProfile(vcard: VCard): Single<Profile>
+    abstract fun peerProfileReceived(accountId: String, peerId: String, vcard: File): Single<Profile>
+    abstract fun accountProfileReceived(accountId: String, vcardFile: File): Single<Profile>
+    abstract fun base64ToBitmap(base64: String?): Any?
+
+    companion object {
+        const val MAX_SIZE_SIP = 256 * 1024
+        const val MAX_SIZE_REQUEST = 16 * 1024
+    }
+}
\ No newline at end of file
diff --git a/ring-android/libringclient/src/main/java/net/jami/settings/AdvancedAccountPresenter.kt b/ring-android/libringclient/src/main/java/net/jami/settings/AdvancedAccountPresenter.kt
new file mode 100644
index 000000000..0a8b622a6
--- /dev/null
+++ b/ring-android/libringclient/src/main/java/net/jami/settings/AdvancedAccountPresenter.kt
@@ -0,0 +1,97 @@
+/*
+ *  Copyright (C) 2004-2021 Savoir-faire Linux Inc.
+ *
+ *  Author: Hadrien De Sousa <hadrien.desousa@savoirfairelinux.com>
+ *
+ *  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, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+package net.jami.settings
+
+import net.jami.model.Account
+import net.jami.model.ConfigKey
+import net.jami.mvp.RootPresenter
+import net.jami.services.AccountService
+import net.jami.services.ConversationFacade
+import net.jami.utils.Log
+import java.net.NetworkInterface
+import java.net.SocketException
+import java.util.*
+import javax.inject.Inject
+
+class AdvancedAccountPresenter @Inject constructor(
+    private var mConversationFacade: ConversationFacade,
+    private var mAccountService: AccountService
+) : RootPresenter<AdvancedAccountView>() {
+    private var mAccount: Account? = null
+    fun init(accountId: String?) {
+        mAccount = mAccountService.getAccount(accountId)
+        if (mAccount != null) {
+            view!!.initView(mAccount!!.config, networkInterfaces)
+        }
+    }
+
+    fun twoStatePreferenceChanged(configKey: ConfigKey?, newValue: Any) {
+        mAccount!!.setDetail(configKey!!, newValue.toString())
+        updateAccount()
+    }
+
+    fun passwordPreferenceChanged(configKey: ConfigKey?, newValue: Any) {
+        mAccount!!.setDetail(configKey!!, newValue.toString())
+        updateAccount()
+    }
+
+    fun preferenceChanged(configKey: ConfigKey, newValue: Any) {
+        var newValue = newValue
+        if (configKey === ConfigKey.AUDIO_PORT_MAX || configKey === ConfigKey.AUDIO_PORT_MIN) {
+            newValue = adjustRtpRange(Integer.valueOf(newValue as String))
+        }
+        mAccount!!.setDetail(configKey, newValue.toString())
+        updateAccount()
+    }
+
+    private fun updateAccount() {
+        mAccountService.setCredentials(mAccount!!.accountID, mAccount!!.credentialsHashMapList)
+        mAccountService.setAccountDetails(mAccount!!.accountID, mAccount!!.details)
+    }
+
+    private fun adjustRtpRange(newValue: Int): String {
+        if (newValue < 1024) {
+            return "1024"
+        }
+        return if (newValue > 65534) "65534" else newValue.toString()
+    }
+
+    private val networkInterfaces: ArrayList<CharSequence>
+        get() {
+            val result = ArrayList<CharSequence>()
+            result.add("default")
+            try {
+                val list = NetworkInterface.getNetworkInterfaces()
+                while (list.hasMoreElements()) {
+                    val i = list.nextElement()
+                    if (i.isUp) {
+                        result.add(i.displayName)
+                    }
+                }
+            } catch (e: SocketException) {
+                Log.e(TAG, "Error enumerating interfaces: ", e)
+            }
+            return result
+        }
+
+    companion object {
+        val TAG = AdvancedAccountPresenter::class.simpleName!!
+    }
+}
\ No newline at end of file
diff --git a/ring-android/libringclient/src/main/java/net/jami/account/HomeAccountCreationView.java b/ring-android/libringclient/src/main/java/net/jami/settings/AdvancedAccountView.kt
similarity index 80%
rename from ring-android/libringclient/src/main/java/net/jami/account/HomeAccountCreationView.java
rename to ring-android/libringclient/src/main/java/net/jami/settings/AdvancedAccountView.kt
index 6ff690b51..51b387b14 100644
--- a/ring-android/libringclient/src/main/java/net/jami/account/HomeAccountCreationView.java
+++ b/ring-android/libringclient/src/main/java/net/jami/settings/AdvancedAccountView.kt
@@ -17,14 +17,11 @@
  *  along with this program; if not, write to the Free Software
  *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
+package net.jami.settings
 
-package net.jami.account;
+import net.jami.model.AccountConfig
+import java.util.*
 
-public interface HomeAccountCreationView {
-
-    void goToAccountCreation();
-
-    void goToAccountLink();
-
-    void goToAccountConnect();
-}
+interface AdvancedAccountView {
+    fun initView(config: AccountConfig, networkInterfaces: ArrayList<CharSequence>)
+}
\ No newline at end of file
diff --git a/ring-android/libringclient/src/main/java/net/jami/settings/GeneralAccountPresenter.kt b/ring-android/libringclient/src/main/java/net/jami/settings/GeneralAccountPresenter.kt
new file mode 100644
index 000000000..620e6833a
--- /dev/null
+++ b/ring-android/libringclient/src/main/java/net/jami/settings/GeneralAccountPresenter.kt
@@ -0,0 +1,129 @@
+/*
+ *  Copyright (C) 2004-2021 Savoir-faire Linux Inc.
+ *
+ *  Author: Hadrien De Sousa <hadrien.desousa@savoirfairelinux.com>
+ *  Author: Adrien Béraud <adrien.beraud@savoirfairelinux.com>
+ *
+ *  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, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+package net.jami.settings
+
+import io.reactivex.rxjava3.core.Scheduler
+import net.jami.model.Account
+import net.jami.model.ConfigKey
+import net.jami.mvp.RootPresenter
+import net.jami.services.AccountService
+import net.jami.services.HardwareService
+import net.jami.services.PreferencesService
+import net.jami.utils.Log
+import net.jami.utils.Tuple
+import javax.inject.Inject
+import javax.inject.Named
+
+class GeneralAccountPresenter @Inject internal constructor(
+    private val mAccountService: AccountService,
+    private val mHardwareService: HardwareService,
+    private val mPreferenceService: PreferencesService,
+    @param:Named("UiScheduler") private val mUiScheduler: Scheduler
+) : RootPresenter<GeneralAccountView>() {
+    private var mAccount: Account? = null
+
+    // Init with current account
+    fun init() {
+        init(mAccountService.currentAccount)
+    }
+
+    fun init(accountId: String?) {
+        init(mAccountService.getAccount(accountId))
+    }
+
+    private fun init(account: Account?) {
+        mCompositeDisposable.clear()
+        mAccount = account
+        if (account != null) {
+            if (account.isJami) {
+                view!!.addJamiPreferences(account.accountID)
+            } else {
+                view!!.addSipPreferences()
+            }
+            view!!.accountChanged(account)
+            mCompositeDisposable.add(mAccountService.getObservableAccount(account.accountID)
+                .observeOn(mUiScheduler)
+                .subscribe { acc: Account -> view!!.accountChanged(acc) })
+            mCompositeDisposable.add(
+                mHardwareService.maxResolutions
+                    .observeOn(mUiScheduler)
+                    .subscribe({ res: Tuple<Int?, Int?> ->
+                            if (res.first == null) {
+                                view?.updateResolutions(null, mPreferenceService.resolution)
+                            } else {
+                                view?.updateResolutions(res, mPreferenceService.resolution)
+                            }
+                        },
+                        { view?.updateResolutions(null, mPreferenceService.resolution) })
+            )
+        } else {
+            Log.e(TAG, "init: No currentAccount available")
+            view?.finish()
+        }
+    }
+
+    fun setEnabled(enabled: Boolean) {
+        mAccount!!.isEnabled = enabled
+        mAccountService.setAccountEnabled(mAccount!!.accountID, enabled)
+    }
+
+    fun twoStatePreferenceChanged(configKey: ConfigKey, newValue: Any) {
+        if (configKey === ConfigKey.ACCOUNT_ENABLE) {
+            setEnabled(newValue as Boolean)
+        } else {
+            mAccount!!.setDetail(configKey, newValue.toString())
+            updateAccount()
+        }
+    }
+
+    fun passwordPreferenceChanged(configKey: ConfigKey, newValue: Any) {
+        if (mAccount!!.isSip) {
+            mAccount!!.credentials[0].setDetail(configKey, newValue.toString())
+        }
+        updateAccount()
+    }
+
+    fun userNameChanged(configKey: ConfigKey, newValue: Any) {
+        if (mAccount!!.isSip) {
+            mAccount!!.setDetail(configKey, newValue.toString())
+            mAccount!!.credentials[0].setDetail(configKey, newValue.toString())
+        }
+        updateAccount()
+    }
+
+    fun preferenceChanged(configKey: ConfigKey, newValue: Any) {
+        mAccount!!.setDetail(configKey, newValue.toString())
+        updateAccount()
+    }
+
+    private fun updateAccount() {
+        mAccountService.setCredentials(mAccount!!.accountID, mAccount!!.credentialsHashMapList)
+        mAccountService.setAccountDetails(mAccount!!.accountID, mAccount!!.details)
+    }
+
+    fun removeAccount() {
+        mAccountService.removeAccount(mAccount!!.accountID)
+    }
+
+    companion object {
+        private val TAG = GeneralAccountPresenter::class.simpleName!!
+    }
+}
\ No newline at end of file
diff --git a/ring-android/libringclient/src/main/java/net/jami/settings/GeneralAccountView.kt b/ring-android/libringclient/src/main/java/net/jami/settings/GeneralAccountView.kt
new file mode 100644
index 000000000..7e69d0919
--- /dev/null
+++ b/ring-android/libringclient/src/main/java/net/jami/settings/GeneralAccountView.kt
@@ -0,0 +1,31 @@
+/*
+ *  Copyright (C) 2004-2021 Savoir-faire Linux Inc.
+ *
+ *  Author: Hadrien De Sousa <hadrien.desousa@savoirfairelinux.com>
+ *
+ *  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, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+package net.jami.settings
+
+import net.jami.model.Account
+import net.jami.utils.Tuple
+
+interface GeneralAccountView {
+    fun addJamiPreferences(accountId: String)
+    fun addSipPreferences()
+    fun accountChanged(account: Account)
+    fun finish()
+    fun updateResolutions(maxResolution: Tuple<Int?, Int?>?, currentResolution: Int)
+}
\ No newline at end of file
diff --git a/ring-android/libringclient/src/main/java/net/jami/settings/MediaPreferencePresenter.kt b/ring-android/libringclient/src/main/java/net/jami/settings/MediaPreferencePresenter.kt
new file mode 100644
index 000000000..ea56fe7f8
--- /dev/null
+++ b/ring-android/libringclient/src/main/java/net/jami/settings/MediaPreferencePresenter.kt
@@ -0,0 +1,83 @@
+/*
+ *  Copyright (C) 2004-2021 Savoir-faire Linux Inc.
+ *
+ *  Author: Hadrien De Sousa <hadrien.desousa@savoirfairelinux.com>
+ *
+ *  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, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+package net.jami.settings
+
+import io.reactivex.rxjava3.core.Scheduler
+import net.jami.model.Account
+import net.jami.model.Codec
+import net.jami.model.ConfigKey
+import net.jami.mvp.RootPresenter
+import net.jami.services.AccountService
+import net.jami.services.DeviceRuntimeService
+import net.jami.utils.Log
+import java.util.*
+import javax.inject.Inject
+import javax.inject.Named
+
+class MediaPreferencePresenter @Inject constructor(
+    private var mAccountService: AccountService,
+    private var mDeviceRuntimeService: DeviceRuntimeService,
+    @Named("UiScheduler")
+    private var mUiScheduler: Scheduler
+) : RootPresenter<MediaPreferenceView>() {
+    private var mAccount: Account? = null
+
+    fun init(accountId: String) {
+        mAccount = mAccountService.getAccount(accountId)
+        mCompositeDisposable.clear()
+        mCompositeDisposable.add(mAccountService
+            .getObservableAccount(accountId)
+            .switchMapSingle { account: Account ->
+                mAccountService.getCodecList(accountId)
+                    .observeOn(mUiScheduler)
+                    .doOnSuccess { codecList: List<Codec> ->
+                        val audioCodec = ArrayList<Codec>()
+                        val videoCodec = ArrayList<Codec>()
+                        for (codec in codecList) {
+                            if (codec.type === Codec.Type.AUDIO) {
+                                audioCodec.add(codec)
+                            } else if (codec.type === Codec.Type.VIDEO) {
+                                videoCodec.add(codec)
+                            }
+                        }
+                        view?.accountChanged(account, audioCodec, videoCodec)
+                    }
+            }
+            .subscribe({ }) { Log.e(TAG, "Error loading codec list") })
+    }
+
+    fun codecChanged(codecs: ArrayList<Long>) {
+        mAccountService.setActiveCodecList(mAccount!!.accountID, codecs)
+    }
+
+    fun videoPreferenceChanged(key: ConfigKey, newValue: Any) {
+        mAccount!!.setDetail(key, newValue.toString())
+        updateAccount()
+    }
+
+    private fun updateAccount() {
+        mAccountService.setCredentials(mAccount!!.accountID, mAccount!!.credentialsHashMapList)
+        mAccountService.setAccountDetails(mAccount!!.accountID, mAccount!!.details)
+    }
+
+    companion object {
+        val TAG = MediaPreferencePresenter::class.simpleName!!
+    }
+}
\ No newline at end of file
diff --git a/ring-android/app/src/main/java/cx/ring/fragments/MediaPreferenceView.java b/ring-android/libringclient/src/main/java/net/jami/settings/MediaPreferenceView.java
similarity index 97%
rename from ring-android/app/src/main/java/cx/ring/fragments/MediaPreferenceView.java
rename to ring-android/libringclient/src/main/java/net/jami/settings/MediaPreferenceView.java
index 302054563..4f0da2351 100644
--- a/ring-android/app/src/main/java/cx/ring/fragments/MediaPreferenceView.java
+++ b/ring-android/libringclient/src/main/java/net/jami/settings/MediaPreferenceView.java
@@ -17,7 +17,7 @@
  *  along with this program; if not, write to the Free Software
  *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
-package cx.ring.fragments;
+package net.jami.settings;
 
 import java.util.ArrayList;
 
diff --git a/ring-android/libringclient/src/main/java/net/jami/settings/SettingsPresenter.java b/ring-android/libringclient/src/main/java/net/jami/settings/SettingsPresenter.java
deleted file mode 100644
index d46793bd7..000000000
--- a/ring-android/libringclient/src/main/java/net/jami/settings/SettingsPresenter.java
+++ /dev/null
@@ -1,78 +0,0 @@
-/*
- *  Copyright (C) 2004-2021 Savoir-faire Linux Inc.
- *
- *  Author: Thibault Wittemberg <thibault.wittemberg@savoirfairelinux.com>
- *  Author: Adrien Béraud <adrien.beraud@savoirfairelinux.com>
- *
- *  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, write to the Free Software
- *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-package net.jami.settings;
-
-import javax.inject.Inject;
-import javax.inject.Named;
-
-import net.jami.services.ConversationFacade;
-import net.jami.model.Settings;
-import net.jami.mvp.GenericView;
-import net.jami.mvp.RootPresenter;
-import net.jami.services.PreferencesService;
-import net.jami.utils.Log;
-
-import io.reactivex.rxjava3.core.Scheduler;
-
-public class SettingsPresenter extends RootPresenter<GenericView<Settings>>  {
-
-    private final PreferencesService mPreferencesService;
-    private final Scheduler mUiScheduler;
-    private final ConversationFacade mConversationFacade;
-
-    private final static String TAG = SettingsPresenter.class.getSimpleName();
-
-
-    @Inject
-    public SettingsPresenter(PreferencesService preferencesService, ConversationFacade conversationFacade, @Named("UiScheduler") Scheduler uiScheduler) {
-        mPreferencesService = preferencesService;
-        mConversationFacade = conversationFacade;
-        mUiScheduler = uiScheduler;
-    }
-
-    @Override
-    public void bindView(GenericView<Settings> view) {
-        super.bindView(view);
-        mCompositeDisposable.add(mPreferencesService.getSettingsSubject()
-                .subscribeOn(mUiScheduler)
-                .subscribe(settings -> getView().showViewModel(settings)));
-    }
-
-    public void loadSettings() {
-        mPreferencesService.getSettings();
-    }
-
-    public void saveSettings(Settings settings) {
-        mPreferencesService.setSettings(settings);
-    }
-
-    public void clearHistory() {
-        mCompositeDisposable.add(mConversationFacade.clearAllHistory().subscribe(() -> {}, e -> Log.e(TAG, "Error clearing app history", e)));
-    }
-
-    public void setDarkMode(boolean isChecked) {
-        mPreferencesService.setDarkMode(isChecked);
-    }
-
-    public boolean getDarkMode() {
-        return mPreferencesService.getDarkMode();
-    }
-}
diff --git a/ring-android/libringclient/src/main/java/net/jami/settings/SettingsPresenter.kt b/ring-android/libringclient/src/main/java/net/jami/settings/SettingsPresenter.kt
new file mode 100644
index 000000000..6c9b9e6bb
--- /dev/null
+++ b/ring-android/libringclient/src/main/java/net/jami/settings/SettingsPresenter.kt
@@ -0,0 +1,68 @@
+/*
+ *  Copyright (C) 2004-2021 Savoir-faire Linux Inc.
+ *
+ *  Author: Thibault Wittemberg <thibault.wittemberg@savoirfairelinux.com>
+ *  Author: Adrien Béraud <adrien.beraud@savoirfairelinux.com>
+ *
+ *  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, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+package net.jami.settings
+
+import io.reactivex.rxjava3.core.Scheduler
+import net.jami.model.Settings
+import net.jami.mvp.GenericView
+import net.jami.mvp.RootPresenter
+import net.jami.services.ConversationFacade
+import net.jami.services.PreferencesService
+import net.jami.utils.Log
+import javax.inject.Inject
+import javax.inject.Named
+
+class SettingsPresenter @Inject constructor(
+    private val mPreferencesService: PreferencesService,
+    private val mConversationFacade: ConversationFacade,
+    @param:Named("UiScheduler") private val mUiScheduler: Scheduler
+) : RootPresenter<GenericView<Settings>>() {
+    override fun bindView(view: GenericView<Settings>) {
+        super.bindView(view)
+        mCompositeDisposable.add(mPreferencesService.settingsSubject
+            .subscribeOn(mUiScheduler)
+            .subscribe { settings: Settings -> this.view?.showViewModel(settings) })
+    }
+
+    fun loadSettings() {
+        mPreferencesService.settings
+    }
+
+    fun saveSettings(settings: Settings) {
+        mPreferencesService.settings = settings
+    }
+
+    fun clearHistory() {
+        mCompositeDisposable.add(
+            mConversationFacade.clearAllHistory()
+                .subscribe({}) { e -> Log.e(TAG, "Error clearing app history", e) })
+    }
+
+    var darkMode: Boolean
+        get() = mPreferencesService.darkMode
+        set(isChecked) {
+            mPreferencesService.darkMode = isChecked
+        }
+
+    companion object {
+        private val TAG = SettingsPresenter::class.simpleName!!
+    }
+}
\ No newline at end of file
diff --git a/ring-android/libringclient/src/main/java/net/jami/share/SharePresenter.java b/ring-android/libringclient/src/main/java/net/jami/share/SharePresenter.java
deleted file mode 100644
index 91021983c..000000000
--- a/ring-android/libringclient/src/main/java/net/jami/share/SharePresenter.java
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- *  Copyright (C) 2004-2021 Savoir-faire Linux Inc.
- *
- *  Author: Thibault Wittemberg <thibault.wittemberg@savoirfairelinux.com>
- *  Author: Adrien Béraud <adrien.beraud@savoirfairelinux.com>
- *
- *  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, write to the Free Software
- *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-package net.jami.share;
-
-import net.jami.services.AccountService;
-
-import javax.inject.Inject;
-import javax.inject.Named;
-
-import net.jami.mvp.GenericView;
-import net.jami.mvp.RootPresenter;
-
-import io.reactivex.rxjava3.core.Scheduler;
-import io.reactivex.rxjava3.schedulers.Schedulers;
-
-public class SharePresenter extends RootPresenter<GenericView<ShareViewModel>> {
-    private final AccountService mAccountService;
-    private final Scheduler mUiScheduler;
-
-    @Inject
-    public SharePresenter(AccountService accountService, @Named("UiScheduler") Scheduler uiScheduler) {
-        mAccountService = accountService;
-        mUiScheduler = uiScheduler;
-    }
-
-    @Override
-    public void bindView(GenericView<ShareViewModel> view) {
-        super.bindView(view);
-        mCompositeDisposable.add(mAccountService
-                .getCurrentAccountSubject()
-                .map(ShareViewModel::new)
-                .subscribeOn(Schedulers.computation())
-                .observeOn(mUiScheduler)
-                .subscribe(this::loadContactInformation));
-    }
-
-    private void loadContactInformation(ShareViewModel model) {
-        GenericView<ShareViewModel> view = getView();
-        if (view != null) {
-            view.showViewModel(model);
-        }
-    }
-}
diff --git a/ring-android/libringclient/src/main/java/net/jami/share/SharePresenter.kt b/ring-android/libringclient/src/main/java/net/jami/share/SharePresenter.kt
new file mode 100644
index 000000000..ffb6719a4
--- /dev/null
+++ b/ring-android/libringclient/src/main/java/net/jami/share/SharePresenter.kt
@@ -0,0 +1,49 @@
+/*
+ *  Copyright (C) 2004-2021 Savoir-faire Linux Inc.
+ *
+ *  Author: Thibault Wittemberg <thibault.wittemberg@savoirfairelinux.com>
+ *  Author: Adrien Béraud <adrien.beraud@savoirfairelinux.com>
+ *
+ *  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, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+package net.jami.share
+
+import io.reactivex.rxjava3.core.Scheduler
+import io.reactivex.rxjava3.schedulers.Schedulers
+import net.jami.model.Account
+import net.jami.mvp.GenericView
+import net.jami.mvp.RootPresenter
+import net.jami.services.AccountService
+import javax.inject.Inject
+import javax.inject.Named
+
+class SharePresenter @Inject constructor(
+    private val mAccountService: AccountService,
+    @param:Named("UiScheduler") private val mUiScheduler: Scheduler
+) : RootPresenter<GenericView<ShareViewModel>>() {
+    override fun bindView(view: GenericView<ShareViewModel>) {
+        super.bindView(view)
+        mCompositeDisposable.add(mAccountService
+            .currentAccountSubject
+            .map { account: Account -> ShareViewModel(account) }
+            .subscribeOn(Schedulers.computation())
+            .observeOn(mUiScheduler)
+            .subscribe { model: ShareViewModel -> loadContactInformation(model) })
+    }
+
+    private fun loadContactInformation(model: ShareViewModel) {
+        view?.showViewModel(model)
+    }
+}
\ No newline at end of file
diff --git a/ring-android/libringclient/src/main/java/net/jami/share/ShareViewModel.java b/ring-android/libringclient/src/main/java/net/jami/share/ShareViewModel.java
deleted file mode 100644
index 2acf2f16e..000000000
--- a/ring-android/libringclient/src/main/java/net/jami/share/ShareViewModel.java
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- *  Copyright (C) 2004-2021 Savoir-faire Linux Inc.
- *
- *  Author: Thibault Wittemberg <thibault.wittemberg@savoirfairelinux.com>
- *
- *  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, write to the Free Software
- *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-package net.jami.share;
-
-import net.jami.utils.QRCodeUtils;
-import net.jami.model.Account;
-
-public class ShareViewModel {
-
-    private final String shareUri;
-    private final String displayUri;
-    private final Account mAccount;
-
-    public ShareViewModel(Account account) {
-        shareUri = account.getUri();
-        displayUri = account.getDisplayUri();
-        mAccount = account;
-    }
-
-    public net.jami.utils.QRCodeUtils.QRCodeData getAccountQRCodeData(final int foregroundColor, final int backgroundColor) {
-        return QRCodeUtils.encodeStringAsQRCodeData(shareUri, foregroundColor, backgroundColor);
-    }
-
-    public String getAccountShareUri() {
-        return shareUri;
-    }
-
-    public String getAccountDisplayUri() {
-        return displayUri;
-    }
-
-    public Account getAccount() {
-        return mAccount;
-    }
-
-
-}
diff --git a/ring-android/libringclient/src/main/java/net/jami/share/ShareViewModel.kt b/ring-android/libringclient/src/main/java/net/jami/share/ShareViewModel.kt
new file mode 100644
index 000000000..55d2a2021
--- /dev/null
+++ b/ring-android/libringclient/src/main/java/net/jami/share/ShareViewModel.kt
@@ -0,0 +1,34 @@
+/*
+ *  Copyright (C) 2004-2021 Savoir-faire Linux Inc.
+ *
+ *  Author: Thibault Wittemberg <thibault.wittemberg@savoirfairelinux.com>
+ *
+ *  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, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+package net.jami.share
+
+import net.jami.model.Account
+import net.jami.utils.QRCodeUtils.QRCodeData
+import net.jami.utils.QRCodeUtils
+
+class ShareViewModel(val account: Account) {
+    private val accountShareUri: String? = account.uri
+    val accountDisplayUri: String? = account.displayUri
+
+    fun getAccountQRCodeData(foregroundColor: Int, backgroundColor: Int): QRCodeData? {
+        return QRCodeUtils.encodeStringAsQRCodeData(accountShareUri, foregroundColor, backgroundColor)
+    }
+
+}
\ No newline at end of file
diff --git a/ring-android/libringclient/src/main/java/net/jami/smartlist/SmartListPresenter.kt b/ring-android/libringclient/src/main/java/net/jami/smartlist/SmartListPresenter.kt
index 5787d744f..cdec0ddd7 100644
--- a/ring-android/libringclient/src/main/java/net/jami/smartlist/SmartListPresenter.kt
+++ b/ring-android/libringclient/src/main/java/net/jami/smartlist/SmartListPresenter.kt
@@ -77,7 +77,7 @@ class SmartListPresenter @Inject constructor(
         startConversation(viewModel.accountId, viewModel.uri)
     }
 
-    fun conversationLongClicked(smartListViewModel: SmartListViewModel?) {
+    fun conversationLongClicked(smartListViewModel: SmartListViewModel) {
         view!!.displayConversationDialog(smartListViewModel)
     }
 
@@ -93,7 +93,7 @@ class SmartListPresenter @Inject constructor(
         }
     }
 
-    fun startConversation(uri: Uri?) {
+    fun startConversation(uri: Uri) {
         view!!.goToConversation(mAccount!!.accountID, uri)
     }
 
@@ -143,16 +143,16 @@ class SmartListPresenter @Inject constructor(
         view!!.setLoading(true)
         mCompositeDisposable.add(conversations
             .switchMap { viewModels: List<Observable<SmartListViewModel>> ->
-                if (viewModels.isEmpty()) SmartListViewModel.EMPTY_RESULTS else Observable.combineLatest<SmartListViewModel, List<SmartListViewModel>>(
-                    viewModels
-                ) { obs: Array<Any> -> //obs.map { it as SmartListViewModel }
+                if (viewModels.isEmpty())
+                    SmartListViewModel.EMPTY_RESULTS
+                else Observable.combineLatest(viewModels) { obs: Array<Any> -> //obs.map { it as SmartListViewModel }
                     val vms: MutableList<SmartListViewModel> = ArrayList(obs.size)
                     for (ob in obs) vms.add(ob as SmartListViewModel)
                     vms
                 }.throttleLatest(150, TimeUnit.MILLISECONDS, mUiScheduler)
             }
             .observeOn(mUiScheduler)
-            .subscribe({ viewModels: List<SmartListViewModel> ->
+            .subscribe({ viewModels: MutableList<SmartListViewModel> ->
                 val view = view
                 view!!.setLoading(false)
                 if (viewModels.isEmpty()) {
diff --git a/ring-android/libringclient/src/main/java/net/jami/smartlist/SmartListView.java b/ring-android/libringclient/src/main/java/net/jami/smartlist/SmartListView.java
deleted file mode 100644
index a043e9a5d..000000000
--- a/ring-android/libringclient/src/main/java/net/jami/smartlist/SmartListView.java
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- *  Copyright (C) 2004-2021 Savoir-faire Linux Inc.
- *
- *  Author: Hadrien De Sousa <hadrien.desousa@savoirfairelinux.com>
- *
- *  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, write to the Free Software
- *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-package net.jami.smartlist;
-
-import java.util.List;
-
-import net.jami.model.Uri;
-import net.jami.mvp.BaseView;
-
-import io.reactivex.rxjava3.disposables.CompositeDisposable;
-
-public interface SmartListView extends BaseView {
-
-    void displayChooseNumberDialog(CharSequence[] numbers);
-
-    void displayNoConversationMessage();
-
-    void displayConversationDialog(SmartListViewModel smartListViewModel);
-
-    void displayClearDialog(Uri callContact);
-
-    void displayDeleteDialog(Uri callContact);
-
-    void copyNumber(Uri uri);
-
-    void setLoading(boolean display);
-
-    void displayMenuItem();
-
-    void hideList();
-
-    void hideNoConversationMessage();
-
-    void updateList(List<SmartListViewModel> smartListViewModels, CompositeDisposable parentDisposable);
-    void update(SmartListViewModel model);
-    void update(int position);
-
-    void goToConversation(String accountId, Uri contactId);
-
-    void goToCallActivity(String accountId, Uri conversationUri, String contactId);
-
-    void goToQRFragment();
-
-    void scrollToTop();
-}
diff --git a/ring-android/libringclient/src/main/java/net/jami/smartlist/SmartListView.kt b/ring-android/libringclient/src/main/java/net/jami/smartlist/SmartListView.kt
new file mode 100644
index 000000000..7147e2f6f
--- /dev/null
+++ b/ring-android/libringclient/src/main/java/net/jami/smartlist/SmartListView.kt
@@ -0,0 +1,43 @@
+/*
+ *  Copyright (C) 2004-2021 Savoir-faire Linux Inc.
+ *
+ *  Author: Hadrien De Sousa <hadrien.desousa@savoirfairelinux.com>
+ *
+ *  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, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+package net.jami.smartlist
+
+import io.reactivex.rxjava3.disposables.CompositeDisposable
+import net.jami.model.Uri
+
+interface SmartListView {
+    fun displayChooseNumberDialog(numbers: Array<CharSequence>)
+    fun displayNoConversationMessage()
+    fun displayConversationDialog(smartListViewModel: SmartListViewModel)
+    fun displayClearDialog(conversationUri: Uri)
+    fun displayDeleteDialog(conversationUri: Uri)
+    fun copyNumber(uri: Uri)
+    fun setLoading(loading: Boolean)
+    fun displayMenuItem()
+    fun hideList()
+    fun hideNoConversationMessage()
+    fun updateList(smartListViewModels: MutableList<SmartListViewModel>?, parentDisposable: CompositeDisposable)
+    fun update(model: SmartListViewModel)
+    fun update(position: Int)
+    fun goToConversation(accountId: String, conversationUri: Uri)
+    fun goToCallActivity(accountId: String, conversationUri: Uri, contactId: String)
+    fun goToQRFragment()
+    fun scrollToTop()
+}
\ No newline at end of file
diff --git a/ring-android/libringclient/src/main/java/net/jami/smartlist/SmartListViewModel.kt b/ring-android/libringclient/src/main/java/net/jami/smartlist/SmartListViewModel.kt
index 9e6ab56e8..da6b36570 100644
--- a/ring-android/libringclient/src/main/java/net/jami/smartlist/SmartListViewModel.kt
+++ b/ring-android/libringclient/src/main/java/net/jami/smartlist/SmartListViewModel.kt
@@ -99,7 +99,7 @@ class SmartListViewModel {
         headerTitle = Title.None
     }
 
-    constructor(conversation: Conversation, presence: Boolean) : this(conversation, conversation.contacts, presence) {}
+    constructor(conversation: Conversation, presence: Boolean) : this(conversation, conversation.contacts, presence)
 
     private constructor(title: Title) {
         contactName = null
@@ -166,7 +166,6 @@ class SmartListViewModel {
         val TITLE_CONVERSATIONS: Observable<SmartListViewModel> = Observable.just(SmartListViewModel(Title.Conversations))
         val TITLE_PUBLIC_DIR: Observable<SmartListViewModel> = Observable.just(SmartListViewModel(Title.PublicDirectory))
         val EMPTY_LIST: Single<List<Observable<SmartListViewModel>>> = Single.just(emptyList())
-        @JvmStatic
-        val EMPTY_RESULTS: Observable<List<SmartListViewModel>> = Observable.just(emptyList())
+        val EMPTY_RESULTS: Observable<MutableList<SmartListViewModel>> = Observable.just(ArrayList())
     }
 }
\ No newline at end of file
diff --git a/ring-android/libringclient/src/main/java/net/jami/utils/Log.java b/ring-android/libringclient/src/main/java/net/jami/utils/Log.java
deleted file mode 100644
index e075cb145..000000000
--- a/ring-android/libringclient/src/main/java/net/jami/utils/Log.java
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- *  Copyright (C) 2004-2021 Savoir-faire Linux Inc.
- *
- *  Author: Thibault Wittemberg <thibault.wittemberg@savoirfairelinux.com>
- *
- *  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, write to the Free Software
- *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-package net.jami.utils;
-
-import net.jami.services.LogService;
-
-public class Log {
-    private static LogService mLogService;
-
-    public static void injectLogService(LogService service) {
-        mLogService = service;
-    }
-
-    public static void d(String tag, String message) {
-        mLogService.d(tag, message);
-    }
-
-    public static void e(String tag, String message) {
-        mLogService.e(tag, message);
-    }
-
-    public static void i(String tag, String message) {
-        mLogService.i(tag, message);
-    }
-
-    public static void w(String tag, String message) {
-        mLogService.w(tag, message);
-    }
-
-    public static void d(String tag, String message, Throwable e) {
-        mLogService.d(tag, message, e);
-    }
-
-    public static void e(String tag, String message, Throwable e) {
-        mLogService.e(tag, message, e);
-    }
-
-    public static void i(String tag, String message, Throwable e) {
-        mLogService.i(tag, message, e);
-    }
-
-    public static void w(String tag, String message, Throwable e) {
-        mLogService.w(tag, message, e);
-    }
-
-}
diff --git a/ring-android/libringclient/src/main/java/net/jami/utils/Log.kt b/ring-android/libringclient/src/main/java/net/jami/utils/Log.kt
new file mode 100644
index 000000000..31a979b97
--- /dev/null
+++ b/ring-android/libringclient/src/main/java/net/jami/utils/Log.kt
@@ -0,0 +1,65 @@
+/*
+ *  Copyright (C) 2004-2021 Savoir-faire Linux Inc.
+ *
+ *  Author: Thibault Wittemberg <thibault.wittemberg@savoirfairelinux.com>
+ *
+ *  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, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+package net.jami.utils
+
+import net.jami.services.LogService
+
+object Log {
+    private lateinit var mLogService: LogService
+
+    fun injectLogService(service: LogService) {
+        mLogService = service
+    }
+
+    @JvmStatic
+    fun d(tag: String, message: String) {
+        mLogService.d(tag, message)
+    }
+
+    fun e(tag: String, message: String) {
+        mLogService.e(tag, message)
+    }
+
+    fun i(tag: String, message: String) {
+        mLogService.i(tag, message)
+    }
+
+    @JvmStatic
+    fun w(tag: String, message: String) {
+        mLogService.w(tag, message)
+    }
+
+    fun d(tag: String, message: String, e: Throwable) {
+        mLogService.d(tag, message, e)
+    }
+
+    @JvmStatic
+    fun e(tag: String, message: String, e: Throwable) {
+        mLogService.e(tag, message, e)
+    }
+
+    fun i(tag: String, message: String, e: Throwable) {
+        mLogService.i(tag, message, e)
+    }
+
+    fun w(tag: String, message: String, e: Throwable) {
+        mLogService.w(tag, message, e)
+    }
+}
\ No newline at end of file
diff --git a/ring-android/libringclient/src/main/java/net/jami/utils/NameLookupInputHandler.java b/ring-android/libringclient/src/main/java/net/jami/utils/NameLookupInputHandler.java
deleted file mode 100644
index 3eeb57eda..000000000
--- a/ring-android/libringclient/src/main/java/net/jami/utils/NameLookupInputHandler.java
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- *  Copyright (C) 2004-2021 Savoir-faire Linux Inc.
- *
- *  Author: Adrien Béraud <adrien.beraud@savoirfairelinux.com>
- *
- *  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 net.jami.utils;
-
-import net.jami.services.AccountService;
-
-import java.lang.ref.WeakReference;
-import java.util.Timer;
-import java.util.TimerTask;
-
-public class NameLookupInputHandler {
-    private static final int WAIT_DELAY = 350;
-    private final WeakReference<AccountService> mAccountService;
-    private final String mAccountId;
-    private final Timer timer = new Timer(true);
-    private NameTask lastTask = null;
-
-    public NameLookupInputHandler(AccountService accountService, String accountId) {
-        mAccountService = new WeakReference<>(accountService);
-        mAccountId = accountId;
-    }
-
-    public void enqueueNextLookup(String text) {
-        if (lastTask != null) {
-            lastTask.cancel();
-        }
-        lastTask = new NameTask(text);
-        timer.schedule(lastTask, WAIT_DELAY);
-    }
-
-    private class NameTask extends TimerTask {
-        private final String mTextToLookup;
-
-        NameTask(String name) {
-            mTextToLookup = name;
-        }
-
-        @Override
-        public void run() {
-            final AccountService accountService = mAccountService.get();
-            if (accountService != null) {
-                accountService.lookupName(mAccountId, "", mTextToLookup);
-            }
-        }
-    }
-}
\ No newline at end of file
diff --git a/ring-android/libringclient/src/main/java/net/jami/utils/NameLookupInputHandler.kt b/ring-android/libringclient/src/main/java/net/jami/utils/NameLookupInputHandler.kt
new file mode 100644
index 000000000..f8db27ecd
--- /dev/null
+++ b/ring-android/libringclient/src/main/java/net/jami/utils/NameLookupInputHandler.kt
@@ -0,0 +1,46 @@
+/*
+ *  Copyright (C) 2004-2021 Savoir-faire Linux Inc.
+ *
+ *  Author: Adrien Béraud <adrien.beraud@savoirfairelinux.com>
+ *
+ *  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 net.jami.utils
+
+import net.jami.services.AccountService
+import java.lang.ref.WeakReference
+import java.util.*
+
+class NameLookupInputHandler(accountService: AccountService, accountId: String) {
+    private val mAccountService: WeakReference<AccountService> = WeakReference(accountService)
+    private val mAccountId: String = accountId
+    private val timer = Timer(true)
+    private var lastTask: NameTask? = null
+
+    fun enqueueNextLookup(text: String) {
+        lastTask?.cancel()
+        lastTask = NameTask(text)
+        timer.schedule(lastTask, WAIT_DELAY.toLong())
+    }
+
+    private inner class NameTask(private val mTextToLookup: String) : TimerTask() {
+        override fun run() {
+            mAccountService.get()?.lookupName(mAccountId, "", mTextToLookup)
+        }
+    }
+
+    companion object {
+        private const val WAIT_DELAY = 350
+    }
+}
\ No newline at end of file
diff --git a/ring-android/libringclient/src/main/java/net/jami/utils/QRCodeUtils.java b/ring-android/libringclient/src/main/java/net/jami/utils/QRCodeUtils.java
deleted file mode 100644
index 1a05f3198..000000000
--- a/ring-android/libringclient/src/main/java/net/jami/utils/QRCodeUtils.java
+++ /dev/null
@@ -1,99 +0,0 @@
-/*
- * Copyright (C) 2004-2021 Savoir-faire Linux Inc.
- *
- *  Author: Thibault Wittemberg <thibault.wittemberg@savoirfairelinux.com>
- *
- * 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, write to the Free Software
- *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
-package net.jami.utils;
-
-import com.google.zxing.BarcodeFormat;
-import com.google.zxing.EncodeHintType;
-import com.google.zxing.WriterException;
-import com.google.zxing.common.BitMatrix;
-import com.google.zxing.qrcode.QRCodeWriter;
-
-import java.util.HashMap;
-
-public class QRCodeUtils {
-
-    private final static String TAG = QRCodeUtils.class.getName();
-
-    private final static int QRCODE_IMAGE_SIZE = 256;
-    private final static int QRCODE_IMAGE_PADDING = 1;
-
-    /**
-     * @param input uri to be displayed
-     * @return the resulting data
-     */
-    public static QRCodeData encodeStringAsQRCodeData(String input, final int foregroundColor, final int backgroundColor) {
-
-        if (input == null || input.isEmpty()) {
-            return null;
-        }
-
-        QRCodeWriter qrWriter = new QRCodeWriter();
-        BitMatrix qrImageMatrix;
-        try {
-
-            HashMap<EncodeHintType, Integer> hints = new HashMap<>();
-            hints.put(EncodeHintType.MARGIN, QRCODE_IMAGE_PADDING);
-
-            qrImageMatrix = qrWriter.encode(input, BarcodeFormat.QR_CODE, QRCODE_IMAGE_SIZE, QRCODE_IMAGE_SIZE, hints);
-        } catch (WriterException e) {
-            Log.e(TAG, "Error while encoding QR", e);
-            return null;
-        }
-
-        int qrImageWidth = qrImageMatrix.getWidth();
-        int qrImageHeight = qrImageMatrix.getHeight();
-        int[] pixels = new int[qrImageWidth * qrImageHeight];
-
-        for (int row = 0; row < qrImageHeight; row++) {
-            int offset = row * qrImageWidth;
-            for (int column = 0; column < qrImageWidth; column++) {
-                pixels[offset + column] = qrImageMatrix.get(column, row) ? foregroundColor : backgroundColor;
-            }
-        }
-
-        return new QRCodeData(pixels, qrImageWidth, qrImageHeight);
-    }
-
-    public static class QRCodeData {
-        private final int[] mData;
-        private final int mWidth;
-        private final int mHeight;
-
-        public QRCodeData(int[] data, int width, int height) {
-            mData = data;
-            mWidth = width;
-            mHeight = height;
-        }
-
-        public int[] getData() {
-            return mData;
-        }
-
-        public int getWidth() {
-            return mWidth;
-        }
-
-        public int getHeight() {
-            return mHeight;
-        }
-    }
-
-}
\ No newline at end of file
diff --git a/ring-android/libringclient/src/main/java/net/jami/utils/QRCodeUtils.kt b/ring-android/libringclient/src/main/java/net/jami/utils/QRCodeUtils.kt
new file mode 100644
index 000000000..e08885ab3
--- /dev/null
+++ b/ring-android/libringclient/src/main/java/net/jami/utils/QRCodeUtils.kt
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2004-2021 Savoir-faire Linux Inc.
+ *
+ *  Author: Thibault Wittemberg <thibault.wittemberg@savoirfairelinux.com>
+ *
+ * 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, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+package net.jami.utils
+
+import com.google.zxing.BarcodeFormat
+import com.google.zxing.EncodeHintType
+import com.google.zxing.WriterException
+import com.google.zxing.common.BitMatrix
+import com.google.zxing.qrcode.QRCodeWriter
+import net.jami.utils.Log.e
+import java.util.*
+
+object QRCodeUtils {
+    private val TAG = QRCodeUtils::class.simpleName!!
+    private const val QRCODE_IMAGE_SIZE = 256
+    private const val QRCODE_IMAGE_PADDING = 1
+
+    /**
+     * @param input uri to be displayed
+     * @return the resulting data
+     */
+    fun encodeStringAsQRCodeData(input: String?, foregroundColor: Int, backgroundColor: Int): QRCodeData? {
+        if (input == null || input.isEmpty()) {
+            return null
+        }
+        val qrWriter = QRCodeWriter()
+        val qrImageMatrix: BitMatrix
+        try {
+            val hints = HashMap<EncodeHintType, Int?>()
+            hints[EncodeHintType.MARGIN] = QRCODE_IMAGE_PADDING
+            qrImageMatrix = qrWriter.encode(input, BarcodeFormat.QR_CODE, QRCODE_IMAGE_SIZE, QRCODE_IMAGE_SIZE, hints)
+        } catch (e: WriterException) {
+            e(TAG, "Error while encoding QR", e)
+            return null
+        }
+        val qrImageWidth = qrImageMatrix.getWidth()
+        val qrImageHeight = qrImageMatrix.getHeight()
+        val pixels = IntArray(qrImageWidth * qrImageHeight)
+        for (row in 0 until qrImageHeight) {
+            val offset = row * qrImageWidth
+            for (column in 0 until qrImageWidth) {
+                pixels[offset + column] = if (qrImageMatrix[column, row]) foregroundColor else backgroundColor
+            }
+        }
+        return QRCodeData(pixels, qrImageWidth, qrImageHeight)
+    }
+
+    class QRCodeData(val data: IntArray, val width: Int, val height: Int)
+}
\ No newline at end of file
diff --git a/ring-android/libringclient/src/main/java/net/jami/utils/VCardUtils.kt b/ring-android/libringclient/src/main/java/net/jami/utils/VCardUtils.kt
index 4cd45da4d..89c9d94d8 100644
--- a/ring-android/libringclient/src/main/java/net/jami/utils/VCardUtils.kt
+++ b/ring-android/libringclient/src/main/java/net/jami/utils/VCardUtils.kt
@@ -40,7 +40,7 @@ import java.lang.Exception
 import java.util.HashMap
 
 object VCardUtils {
-    val TAG = VCardUtils::class.simpleName
+    val TAG = VCardUtils::class.simpleName!!
     const val MIME_PROFILE_VCARD = "x-ring/ring.profile.vcard"
     const val VCARD_KEY_MIME_TYPE = "mimeType"
     const val VCARD_KEY_PART = "part"
@@ -48,7 +48,7 @@ object VCardUtils {
     const val LOCAL_USER_VCARD_NAME = "profile.vcf"
     private const val VCARD_MAX_SIZE = 1024L * 1024L * 8
 
-    fun readData(vcard: VCard?): Tuple<String?, ByteArray?> {
+    fun readData(vcard: VCard?): Pair<String?, ByteArray?> {
         var contactName: String? = null
         var photo: ByteArray? = null
         if (vcard != null) {
@@ -67,7 +67,7 @@ object VCardUtils {
                 }
             }
         }
-        return Tuple(contactName, photo)
+        return Pair(contactName, photo)
     }
 
     fun writeData(uri: String?, displayName: String?, picture: ByteArray?): VCard {
@@ -106,7 +106,6 @@ object VCardUtils {
         saveToDisk(vcard, filename, peerProfilePath(filesDir, accountId))
     }
 
-    @JvmStatic
     fun saveLocalProfileToDisk(vcard: VCard, accountId: String, filesDir: File): Single<VCard> {
         return Single.fromCallable {
             saveToDisk(vcard, LOCAL_USER_VCARD_NAME, localProfilePath(filesDir, accountId))
@@ -152,7 +151,6 @@ object VCardUtils {
         }
     }
 
-    @JvmStatic
     fun loadLocalProfileFromDiskWithDefault(filesDir: File, accountId: String): Single<VCard> {
         return loadLocalProfileFromDisk(filesDir, accountId)
             .onErrorReturn { e: Throwable? -> setupDefaultProfile(filesDir, accountId) }
@@ -219,12 +217,22 @@ object VCardUtils {
         return vcard
     }
 
-    fun peerProfileReceived(filesDir: File, accountId: String, peerId: String, vcard: File?): Single<VCard> {
+    fun accountProfileReceived(filesDir: File, accountId: String, vcard: File): Single<VCard> {
+        return Single.fromCallable {
+            val card = loadFromDisk(vcard)!!
+            saveLocalProfileToDisk(card, accountId, filesDir)
+                .subscribeOn(Schedulers.io())
+                .subscribe({}) { e -> Log.e(TAG, "Error while saving vcard", e) }
+            card
+        }.subscribeOn(Schedulers.io())
+    }
+
+    fun peerProfileReceived(filesDir: File, accountId: String, peerId: String, vcard: File): Single<VCard> {
         return Single.fromCallable<VCard> {
             val filename = "$peerId.vcf"
             val peerProfilePath = peerProfilePath(filesDir, accountId)
             val file = File(peerProfilePath, filename)
-            moveFile(vcard!!, file)
+            moveFile(vcard, file)
             loadFromDisk(file)
         }.subscribeOn(Schedulers.io())
     }
diff --git a/ring-android/libringclient/src/main/java/net/jami/wizard/SIPCreationPresenter.java b/ring-android/libringclient/src/main/java/net/jami/wizard/SIPCreationPresenter.java
deleted file mode 100644
index bbf6da804..000000000
--- a/ring-android/libringclient/src/main/java/net/jami/wizard/SIPCreationPresenter.java
+++ /dev/null
@@ -1,210 +0,0 @@
-/*
- *  Copyright (C) 2004-2021 Savoir-faire Linux Inc.
- *
- *  Author: Thibault Wittemberg <thibault.wittemberg@savoirfairelinux.com>
- *  Author: Adrien Béraud <adrien.beraud@savoirfairelinux.com>
- *
- *  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, write to the Free Software
- *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-package net.jami.wizard;
-
-import net.jami.services.AccountService;
-import net.jami.services.DeviceRuntimeService;
-
-import java.util.HashMap;
-import java.util.Map;
-
-import javax.inject.Inject;
-import javax.inject.Named;
-
-import net.jami.model.Account;
-import net.jami.model.AccountConfig;
-import net.jami.model.ConfigKey;
-import net.jami.mvp.RootPresenter;
-import net.jami.mvp.SIPCreationView;
-import net.jami.utils.Log;
-import net.jami.utils.VCardUtils;
-import ezvcard.VCard;
-import ezvcard.property.FormattedName;
-import ezvcard.property.RawProperty;
-import ezvcard.property.Uid;
-import io.reactivex.rxjava3.annotations.NonNull;
-import io.reactivex.rxjava3.core.Scheduler;
-import io.reactivex.rxjava3.observers.DisposableObserver;
-
-public class SIPCreationPresenter extends RootPresenter<SIPCreationView> {
-
-    private static final String TAG = SIPCreationPresenter.class.getSimpleName();
-
-    private net.jami.services.AccountService mAccountService;
-
-    private net.jami.services.DeviceRuntimeService mDeviceService;
-
-    private Account mAccount;
-
-    @Inject
-    @Named("UiScheduler")
-    protected Scheduler mUiScheduler;
-
-
-    @Inject
-    public SIPCreationPresenter(AccountService mAccountService, DeviceRuntimeService mDeviceService) {
-        this.mAccountService = mAccountService;
-        this.mDeviceService = mDeviceService;
-    }
-
-    @Override
-    public void bindView(SIPCreationView view) {
-        super.bindView(view);
-    }
-
-    @Override
-    public void unbindView() {
-        super.unbindView();
-    }
-
-    /**
-     * Attempts to register the account specified by the form. If there are form errors (invalid or missing fields, etc.), the
-     * errors are presented and no actual creation attempt is made.
-     *
-     * @param hostname      hostname account value
-     * @param username      username account value
-     * @param password      password account value
-     * @param bypassWarning Report eventual warning to the user
-     */
-    public void startCreation(String hostname, String proxy, String username, String password, boolean bypassWarning) {
-
-        getView().resetErrors();
-
-        // Store values at the time of the login attempt.
-        boolean warningIPAccount = false;
-
-        if (hostname != null && hostname.isEmpty()) {
-            warningIPAccount = true;
-        }
-
-        if (!warningIPAccount && (password == null || password.trim().isEmpty())) {
-            getView().showPasswordError();
-            return;
-        }
-
-        if (!warningIPAccount && (username == null || username.trim().isEmpty())) {
-            getView().showUsernameError();
-            return;
-        }
-
-        if (warningIPAccount && !bypassWarning) {
-            getView().showIP2IPWarning();
-        } else {
-            HashMap<String, String> accountDetails = initAccountDetails();
-
-            if (accountDetails != null) {
-                accountDetails.put(net.jami.model.ConfigKey.ACCOUNT_ALIAS.key(), username);
-                if (hostname != null && !hostname.isEmpty()) {
-                    accountDetails.put(ConfigKey.ACCOUNT_HOSTNAME.key(), hostname);
-                    accountDetails.put(ConfigKey.ACCOUNT_ROUTESET.key(), proxy);
-                    accountDetails.put(ConfigKey.ACCOUNT_USERNAME.key(), username);
-                    accountDetails.put(ConfigKey.ACCOUNT_PASSWORD.key(), password);
-                }
-            }
-            registerAccount(accountDetails);
-        }
-    }
-
-    public void removeAccount() {
-        if (mAccount != null) {
-            mAccountService.removeAccount(mAccount.getAccountID());
-            mAccount = null;
-        }
-    }
-
-    private void registerAccount(final HashMap<String, String> accountDetails) {
-        getView().showLoading();
-        mCompositeDisposable.add(
-                mAccountService.addAccount(accountDetails)
-                .observeOn(mUiScheduler)
-                .subscribeWith(new DisposableObserver<Account>() {
-                    @Override
-                    public void onNext(@NonNull Account account) {
-                        mAccount = account;
-                        switch (account.getRegistrationState()) {
-                            case AccountConfig.STATE_REGISTERED:
-                            case net.jami.model.AccountConfig.STATE_SUCCESS:
-                            case AccountConfig.STATE_READY:
-                                saveProfile(account.getAccountID());
-                                getView().showRegistrationSuccess();
-                                dispose();
-                                break;
-                            case AccountConfig.STATE_ERROR_NETWORK:
-                                getView().showRegistrationNetworkError();
-                                dispose();
-                                break;
-                            case AccountConfig.STATE_TRYING:
-                            case AccountConfig.STATE_UNREGISTERED:
-                                return;
-                            default:
-                                getView().showRegistrationError();
-                                dispose();
-                                break;
-                        }
-                    }
-
-                    @Override
-                    public void onError(@NonNull Throwable e) {
-                        getView().showRegistrationError();
-                        dispose();
-                    }
-
-                    @Override
-                    public void onComplete() {
-                        dispose();
-                    }
-                }));
-    }
-
-    private HashMap<String, String> initAccountDetails() {
-
-        try {
-            HashMap<String, String> accountDetails = mAccountService.getAccountTemplate(AccountConfig.ACCOUNT_TYPE_SIP).blockingGet();
-            for (Map.Entry<String, String> e : accountDetails.entrySet()) {
-                Log.d(TAG, "Default account detail: " + e.getKey() + " -> " + e.getValue());
-            }
-
-            accountDetails.put(ConfigKey.VIDEO_ENABLED.key(), Boolean.toString(true));
-
-            //~ Sipinfo is forced for any sipaccount since overrtp is not supported yet.
-            //~ This will have to be removed when it will be supported.
-            accountDetails.put(net.jami.model.ConfigKey.ACCOUNT_DTMF_TYPE.key(), "sipinfo");
-            return accountDetails;
-        } catch (Exception e) {
-            Log.e(TAG, "Error creating account", e);
-            return null;
-        }
-    }
-
-    private void saveProfile(String accountID) {
-        VCard vcard = new VCard();
-        String formattedName = mAccount.getUsername();
-        if (formattedName.isEmpty()) {
-            formattedName = mAccount.getAlias();
-        }
-        vcard.setFormattedName(new FormattedName(formattedName));
-        String vcardUid = formattedName + accountID;
-        vcard.setUid(new Uid(vcardUid));
-        vcard.removeProperties(RawProperty.class);
-        VCardUtils.saveLocalProfileToDisk(vcard, accountID, mDeviceService.provideFilesDir()).subscribe();
-        mAccount.resetProfile();
-    }
-}
\ No newline at end of file
diff --git a/ring-android/libringclient/src/test/java/net/jami/model/ConversationTest.java b/ring-android/libringclient/src/test/java/net/jami/model/ConversationTest.java
deleted file mode 100644
index d854e0adf..000000000
--- a/ring-android/libringclient/src/test/java/net/jami/model/ConversationTest.java
+++ /dev/null
@@ -1,182 +0,0 @@
-/*
- * Copyright (C) 2004-2021 Savoir-faire Linux Inc.
- *
- * Author: Pierre Duchemin <pierre.duchemin@savoirfairelinux.com>
- *
- * 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, write to the Free Software
- *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
-package net.jami.model;
-
-import org.junit.Before;
-import org.junit.Test;
-
-import java.util.Random;
-
-import static org.junit.Assert.assertEquals;
-
-/**
- * Some tests to ensure Conversation integrity
- */
-public class ConversationTest {
-
-    private Conversation conversation;
-
-    @Before
-    public void setUp() {
-        Contact contact = new Contact(Uri.fromString("jami://test"));
-        conversation = new Conversation("", contact);
-    }
-
-    @Test
-    public void init_test() {
-        Contact contact = new Contact(Uri.fromString("jami://test"));
-        conversation = new Conversation("", contact);
-
-        assertEquals(conversation.getContact(), contact);
-    }
-
-    @Test
-    public void getConference() throws Exception {
-    }
-
-    @Test
-    public void addConference() throws Exception {
-
-    }
-
-    @Test
-    public void removeConference() throws Exception {
-    }
-
-    @Test
-    public void setContact() throws Exception {
-    }
-
-    @Test
-    public void isVisible() throws Exception {
-    }
-
-    @Test
-    public void setVisible() throws Exception {
-    }
-
-    @Test
-    public void getContact() throws Exception {
-    }
-
-    @Test
-    public void addHistoryCall() throws Exception {
-        int oldSize = conversation.getAggregateHistory().size();
-        conversation.addCall(new Call("Coucou", "ring:test", "1", conversation, conversation.getContact(), Call.Direction.INCOMING));
-        int newSize = conversation.getAggregateHistory().size();
-
-        assertEquals(0, oldSize);
-        assertEquals(oldSize, newSize - 1);
-    }
-
-    @Test
-    public void addTextMessage() throws Exception {
-        int oldSize = conversation.getAggregateHistory().size();
-        conversation.addTextMessage(new TextMessage( "Coucou", "ring:test", "1", conversation, "Toi"));
-        int newSize = conversation.getAggregateHistory().size();
-
-        assertEquals(0, oldSize);
-        assertEquals(oldSize, newSize - 1);
-    }
-
-    @Test
-    public void updateTextMessage() throws Exception {
-    }
-
-    @Test
-    public void getHistory() throws Exception {
-    }
-
-    @Test
-    public void getAggregateHistory() throws Exception {
-    }
-
-    @Test
-    public void getAccountsUsed() throws Exception {
-    }
-
-    @Test
-    public void getLastAccountUsed() throws Exception {
-    }
-
-    @Test
-    public void getCurrentCall() throws Exception {
-    }
-
-    @Test
-    public void getCurrentCalls() throws Exception {
-    }
-
-    @Test
-    public void getHistoryCalls() throws Exception {
-    }
-
-    @Test
-    public void getUnreadTextMessages() throws Exception {
-    }
-
-    @Test
-    public void getRawHistory() throws Exception {
-    }
-
-    @Test
-    public void findConversationElement() throws Exception {
-    }
-
-    @Test
-    public void addFileTransfer() throws Exception {
-        int oldSize = conversation.getAggregateHistory().size();
-        conversation.addFileTransfer(new DataTransfer(1L, "Coucoou", "ring:sfvfv", "photo.jpg", true, 10L, 0L, 0L));
-        int newSize = conversation.getAggregateHistory().size();
-
-        assertEquals(0, oldSize);
-        assertEquals(oldSize, newSize - 1);
-    }
-
-    @Test
-    public void addFileTransfer1() throws Exception {
-    }
-
-    @Test
-    public void addFileTransfers() throws Exception {
-    }
-
-    @Test
-    public void updateFileTransfer() throws Exception {
-    }
-
-    @Test
-    public void removeAll() throws Exception {
-        int random = new Random().nextInt(20);
-
-        for (int i = 0; i < random; i++) {
-            conversation.addTextMessage(new TextMessage( "Coucou", "ring:test", "1", conversation, "Toi"));
-        }
-        int newSize = conversation.getAggregateHistory().size();
-
-        conversation.removeAll();
-        int lastSize = conversation.getAggregateHistory().size();
-
-        assertEquals(random, newSize);
-        assertEquals(0, lastSize);
-    }
-
-}
diff --git a/ring-android/libringclient/src/test/java/net/jami/model/ConversationTest.kt b/ring-android/libringclient/src/test/java/net/jami/model/ConversationTest.kt
new file mode 100644
index 000000000..8dd563e6b
--- /dev/null
+++ b/ring-android/libringclient/src/test/java/net/jami/model/ConversationTest.kt
@@ -0,0 +1,203 @@
+/*
+ * Copyright (C) 2004-2021 Savoir-faire Linux Inc.
+ *
+ * Author: Pierre Duchemin <pierre.duchemin@savoirfairelinux.com>
+ *
+ * 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, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+package net.jami.model
+
+import net.jami.model.Uri.Companion.fromString
+import org.junit.Assert
+import org.junit.Before
+import org.junit.Test
+import java.util.*
+
+/**
+ * Some tests to ensure Conversation integrity
+ */
+class ConversationTest {
+    private var conversation: Conversation? = null
+    @Before
+    fun setUp() {
+        val contact = Contact(fromString("jami://test"))
+        conversation = Conversation("", contact)
+    }
+
+    @Test
+    fun init_test() {
+        val contact = Contact(fromString("jami://test"))
+        conversation = Conversation("", contact)
+        Assert.assertEquals(conversation!!.contact, contact)
+    }
+
+    @get:Throws(Exception::class)
+    @get:Test
+    val conference: Unit
+        get() {}
+
+    @Test
+    @Throws(Exception::class)
+    fun addConference() {
+    }
+
+    @Test
+    @Throws(Exception::class)
+    fun removeConference() {
+    }
+
+    @Test
+    @Throws(Exception::class)
+    fun setContact() {
+    }
+
+    @get:Throws(Exception::class)
+    @get:Test
+    val isVisible: Unit
+        get() {}
+
+    @Test
+    @Throws(Exception::class)
+    fun setVisible() {
+    }
+
+    @get:Throws(Exception::class)
+    @get:Test
+    val contact: Unit
+        get() {}
+
+    @Test
+    @Throws(Exception::class)
+    fun addHistoryCall() {
+        val oldSize = conversation!!.aggregateHistory.size
+        conversation!!.addCall(
+            Call(
+                "Coucou",
+                "ring:test",
+                "1",
+                conversation,
+                conversation!!.contact,
+                Call.Direction.INCOMING
+            )
+        )
+        val newSize = conversation!!.aggregateHistory.size
+        Assert.assertEquals(0, oldSize.toLong())
+        Assert.assertEquals(oldSize.toLong(), (newSize - 1).toLong())
+    }
+
+    @Test
+    @Throws(Exception::class)
+    fun addTextMessage() {
+        val oldSize = conversation!!.aggregateHistory.size
+        conversation!!.addTextMessage(TextMessage("Coucou", "ring:test", "1", conversation, "Toi"))
+        val newSize = conversation!!.aggregateHistory.size
+        Assert.assertEquals(0, oldSize.toLong())
+        Assert.assertEquals(oldSize.toLong(), (newSize - 1).toLong())
+    }
+
+    @Test
+    @Throws(Exception::class)
+    fun updateTextMessage() {
+    }
+
+    @get:Throws(Exception::class)
+    @get:Test
+    val history: Unit
+        get() {}
+
+    @get:Throws(Exception::class)
+    @get:Test
+    val aggregateHistory: Unit
+        get() {}
+
+    @get:Throws(Exception::class)
+    @get:Test
+    val accountsUsed: Unit
+        get() {}
+
+    @get:Throws(Exception::class)
+    @get:Test
+    val lastAccountUsed: Unit
+        get() {}
+
+    @get:Throws(Exception::class)
+    @get:Test
+    val currentCall: Unit
+        get() {}
+
+    @get:Throws(Exception::class)
+    @get:Test
+    val currentCalls: Unit
+        get() {}
+
+    @get:Throws(Exception::class)
+    @get:Test
+    val historyCalls: Unit
+        get() {}
+
+    @get:Throws(Exception::class)
+    @get:Test
+    val unreadTextMessages: Unit
+        get() {}
+
+    @get:Throws(Exception::class)
+    @get:Test
+    val rawHistory: Unit
+        get() {}
+
+    @Test
+    @Throws(Exception::class)
+    fun findConversationElement() {
+    }
+
+    @Test
+    @Throws(Exception::class)
+    fun addFileTransfer() {
+        val oldSize = conversation!!.aggregateHistory.size
+        conversation!!.addFileTransfer(DataTransfer("1", "Coucoou", "ring:sfvfv", "photo.jpg", true, 10L, 0L, 0L))
+        val newSize = conversation!!.aggregateHistory.size
+        Assert.assertEquals(0, oldSize.toLong())
+        Assert.assertEquals(oldSize.toLong(), (newSize - 1).toLong())
+    }
+
+    @Test
+    @Throws(Exception::class)
+    fun addFileTransfer1() {
+    }
+
+    @Test
+    @Throws(Exception::class)
+    fun addFileTransfers() {
+    }
+
+    @Test
+    @Throws(Exception::class)
+    fun updateFileTransfer() {
+    }
+
+    @Test
+    @Throws(Exception::class)
+    fun removeAll() {
+        val random = Random().nextInt(20)
+        for (i in 0 until random) {
+            conversation!!.addTextMessage(TextMessage("Coucou", "ring:test", "1", conversation, "Toi"))
+        }
+        val newSize = conversation!!.aggregateHistory.size
+        conversation!!.removeAll()
+        val lastSize = conversation!!.aggregateHistory.size
+        Assert.assertEquals(random.toLong(), newSize.toLong())
+        Assert.assertEquals(0, lastSize.toLong())
+    }
+}
\ No newline at end of file
diff --git a/ring-android/libringclient/src/test/java/net/jami/model/UriTest.java b/ring-android/libringclient/src/test/java/net/jami/model/UriTest.java
deleted file mode 100644
index c962b68c9..000000000
--- a/ring-android/libringclient/src/test/java/net/jami/model/UriTest.java
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * Copyright (C) 2004-2021 Savoir-faire Linux Inc.
- *
- * Author: Alexandre Lision <alexandre.lision@savoirfairelinux.com>
- * Author: Pierre Duchemin <pierre.duchemin@savoirfairelinux.com>
- *
- * 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, write to the Free Software
- *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
-package net.jami.model;
-
-import net.jami.utils.Tuple;
-
-import org.junit.Test;
-
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertTrue;
-
-public class UriTest {
-
-    @Test
-    public void testGoodRawString() {
-        String uri = "ring:1234567890123456789012345678901234567890";
-        Uri test = Uri.fromString(uri);
-        assertTrue(test.getRawUriString().contentEquals(uri));
-    }
-
-    @Test
-    public void testBadIPAddress() {
-        assertFalse(Uri.isIpAddress("not an ip"));
-    }
-
-    @Test
-    public void testGoodIPAddress() {
-        assertTrue(Uri.isIpAddress("127.0.0.1"));
-        assertTrue(Uri.isIpAddress("2001:db8:0:85a3:0:0:ac1f:8001"));
-    }
-
-    @Test
-    public void testRingModel() {
-        String uri = "ring:1234567890123456789012345678901234567890";
-        Tuple<Uri, String> test = Uri.fromStringWithName(uri);
-
-        assertNull(test.second);
-        assertTrue(test.first.getScheme().contentEquals("ring:"));
-        assertTrue(test.first.getUri().contentEquals("ring:1234567890123456789012345678901234567890"));
-    }
-
-    @Test
-    public void testSIPModel() {
-        String uri = "100@sipuri";
-        Uri test = Uri.fromString(uri);
-
-        assertTrue(test.getUsername().contentEquals("100"));
-        assertTrue(test.getHost().contentEquals("sipuri"));
-    }
-}
diff --git a/ring-android/libringclient/src/test/java/net/jami/model/UriTest.kt b/ring-android/libringclient/src/test/java/net/jami/model/UriTest.kt
new file mode 100644
index 000000000..4b330caab
--- /dev/null
+++ b/ring-android/libringclient/src/test/java/net/jami/model/UriTest.kt
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2004-2021 Savoir-faire Linux Inc.
+ *
+ * Author: Alexandre Lision <alexandre.lision@savoirfairelinux.com>
+ * Author: Pierre Duchemin <pierre.duchemin@savoirfairelinux.com>
+ *
+ * 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, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+package net.jami.model
+
+import net.jami.model.Uri.Companion.fromString
+import net.jami.model.Uri.Companion.fromStringWithName
+import net.jami.model.Uri.Companion.isIpAddress
+import org.junit.Assert
+import org.junit.Test
+
+class UriTest {
+    @Test
+    fun testGoodRawString() {
+        val uri = "ring:1234567890123456789012345678901234567890"
+        val test = fromString(uri)
+        Assert.assertTrue(test.rawUriString.contentEquals(uri))
+    }
+
+    @Test
+    fun testBadIPAddress() {
+        Assert.assertFalse(isIpAddress("not an ip"))
+    }
+
+    @Test
+    fun testGoodIPAddress() {
+        Assert.assertTrue(isIpAddress("127.0.0.1"))
+        Assert.assertTrue(isIpAddress("2001:db8:0:85a3:0:0:ac1f:8001"))
+    }
+
+    @Test
+    fun testRingModel() {
+        val uri = "ring:1234567890123456789012345678901234567890"
+        val test = fromStringWithName(uri)
+        Assert.assertNull(test.second)
+        Assert.assertTrue(test.first.scheme.contentEquals("ring:"))
+        Assert.assertTrue(test.first.uri.contentEquals("ring:1234567890123456789012345678901234567890"))
+    }
+
+    @Test
+    fun testSIPModel() {
+        val uri = "100@sipuri"
+        val test = fromString(uri)
+        Assert.assertTrue(test.username.contentEquals("100"))
+        Assert.assertTrue(test.host.contentEquals("sipuri"))
+    }
+}
\ No newline at end of file
-- 
GitLab