From ca8d6cb782d833621de1cbf36a5a6a0c12264abe Mon Sep 17 00:00:00 2001 From: Andy Scherzinger Date: Wed, 19 May 2021 18:22:24 +0200 Subject: [PATCH] Move controller to new view binding utilizing logic Signed-off-by: Andy Scherzinger --- .../nextcloud/talk/activities/MainActivity.kt | 2 +- .../MagicPreviewMessageViewHolder.java | 3 +- .../CallNotificationController.java | 5 +- .../talk/controllers/ChatController.kt | 17 +- .../talk/controllers/ContactsController.java | 9 +- .../controllers/ConversationInfoController.kt | 338 ++++++++---------- .../ConversationsListController.java | 10 +- .../talk/controllers/ProfileController.java | 5 +- .../talk/controllers/SettingsController.java | 11 +- .../controllers/base/NewBaseController.kt | 259 ++++++++++++++ .../bottomsheet/CallMenuController.java | 3 +- .../bottomsheet/OperationsMenuController.java | 10 +- .../util/ControllerViewBindingDelegate.kt | 49 +++ .../{User.java => CapabilitiesUtil.java} | 134 +++---- .../nextcloud/talk/models/database/User.kt | 53 +++ .../json/conversations/Conversation.java | 4 +- .../talk/ui/dialog/AttachmentDialog.kt | 3 +- .../com/nextcloud/talk/utils/ApiUtils.java | 24 +- .../DatabaseStorageModule.java | 3 +- .../layout/controller_conversation_info.xml | 33 +- 20 files changed, 640 insertions(+), 335 deletions(-) create mode 100644 app/src/main/java/com/nextcloud/talk/controllers/base/NewBaseController.kt create mode 100644 app/src/main/java/com/nextcloud/talk/controllers/util/ControllerViewBindingDelegate.kt rename app/src/main/java/com/nextcloud/talk/models/database/{User.java => CapabilitiesUtil.java} (65%) create mode 100644 app/src/main/java/com/nextcloud/talk/models/database/User.kt diff --git a/app/src/main/java/com/nextcloud/talk/activities/MainActivity.kt b/app/src/main/java/com/nextcloud/talk/activities/MainActivity.kt index ab78ce354..20d3298cb 100644 --- a/app/src/main/java/com/nextcloud/talk/activities/MainActivity.kt +++ b/app/src/main/java/com/nextcloud/talk/activities/MainActivity.kt @@ -318,7 +318,7 @@ class MainActivity : BaseActivity(), ActionBarProvider { } else { ConductorRemapping.remapChatController( router!!, intent.getLongExtra(BundleKeys.KEY_INTERNAL_USER_ID, -1), - intent.getStringExtra(BundleKeys.KEY_ROOM_TOKEN), intent.extras!!, false + intent.getStringExtra(KEY_ROOM_TOKEN)!!, intent.extras!!, false ) } } diff --git a/app/src/main/java/com/nextcloud/talk/adapters/messages/MagicPreviewMessageViewHolder.java b/app/src/main/java/com/nextcloud/talk/adapters/messages/MagicPreviewMessageViewHolder.java index a77c2e6c9..d0ee7abe9 100644 --- a/app/src/main/java/com/nextcloud/talk/adapters/messages/MagicPreviewMessageViewHolder.java +++ b/app/src/main/java/com/nextcloud/talk/adapters/messages/MagicPreviewMessageViewHolder.java @@ -46,6 +46,7 @@ import com.nextcloud.talk.components.filebrowser.models.BrowserFile; import com.nextcloud.talk.components.filebrowser.models.DavResponse; import com.nextcloud.talk.components.filebrowser.webdav.ReadFilesystemOperation; import com.nextcloud.talk.jobs.DownloadFileToCacheWorker; +import com.nextcloud.talk.models.database.CapabilitiesUtil; import com.nextcloud.talk.models.database.UserEntity; import com.nextcloud.talk.models.json.chat.ChatMessage; import com.nextcloud.talk.utils.AccountUtils; @@ -335,7 +336,7 @@ public class MagicPreviewMessageViewHolder extends MessageHolders.IncomingImageM String baseUrl = message.activeUser.getBaseUrl(); String userId = message.activeUser.getUserId(); - String attachmentFolder = message.activeUser.getAttachmentFolder(); + String attachmentFolder = CapabilitiesUtil.getAttachmentFolder(message.activeUser); String fileName = message.getSelectedIndividualHashMap().get("name"); String mimetype = message.getSelectedIndividualHashMap().get("mimetype"); diff --git a/app/src/main/java/com/nextcloud/talk/controllers/CallNotificationController.java b/app/src/main/java/com/nextcloud/talk/controllers/CallNotificationController.java index e6e64f84e..3f3530af2 100644 --- a/app/src/main/java/com/nextcloud/talk/controllers/CallNotificationController.java +++ b/app/src/main/java/com/nextcloud/talk/controllers/CallNotificationController.java @@ -64,6 +64,7 @@ import com.nextcloud.talk.controllers.base.BaseController; import com.nextcloud.talk.events.CallNotificationClick; import com.nextcloud.talk.events.ConfigurationChangeEvent; import com.nextcloud.talk.models.RingtoneSettings; +import com.nextcloud.talk.models.database.CapabilitiesUtil; import com.nextcloud.talk.models.database.UserEntity; import com.nextcloud.talk.models.json.conversations.Conversation; import com.nextcloud.talk.models.json.conversations.RoomOverall; @@ -278,7 +279,9 @@ public class CallNotificationController extends BaseController { runAllThings(); if (apiVersion >= 3) { - boolean hasCallFlags = userBeingCalled.hasSpreedFeatureCapability("conversation-call-flags"); + boolean hasCallFlags = + CapabilitiesUtil.hasSpreedFeatureCapability(userBeingCalled, + "conversation-call-flags"); if (hasCallFlags) { if (isInCallWithVideo(currentConversation.callFlag)) { incomingCallVoiceOrVideoTextView.setText(String.format(getResources().getString(R.string.nc_call_video), diff --git a/app/src/main/java/com/nextcloud/talk/controllers/ChatController.kt b/app/src/main/java/com/nextcloud/talk/controllers/ChatController.kt index 29ac0f3a7..9beffa2f2 100644 --- a/app/src/main/java/com/nextcloud/talk/controllers/ChatController.kt +++ b/app/src/main/java/com/nextcloud/talk/controllers/ChatController.kt @@ -99,6 +99,7 @@ import com.nextcloud.talk.controllers.base.BaseController import com.nextcloud.talk.events.UserMentionClickEvent import com.nextcloud.talk.events.WebSocketCommunicationEvent import com.nextcloud.talk.jobs.UploadAndShareFilesWorker +import com.nextcloud.talk.models.database.CapabilitiesUtil import com.nextcloud.talk.models.database.UserEntity import com.nextcloud.talk.models.json.chat.ChatMessage import com.nextcloud.talk.models.json.chat.ChatOverall @@ -297,7 +298,8 @@ class ChatController(args: Bundle) : } private fun getRoomInfo() { - val shouldRepeat = conversationUser?.hasSpreedFeatureCapability("webinary-lobby") ?: false + val shouldRepeat = CapabilitiesUtil.hasSpreedFeatureCapability(conversationUser, "webinary-lobby") ?: + false if (shouldRepeat) { checkingLobbyStatus = true } @@ -540,7 +542,7 @@ class ChatController(args: Bundle) : }) val filters = arrayOfNulls(1) - val lengthFilter = conversationUser?.messageMaxLength ?: 1000 + val lengthFilter = CapabilitiesUtil.getMessageMaxLength(conversationUser) ?: 1000 filters[0] = InputFilter.LengthFilter(lengthFilter) messageInput?.filters = filters @@ -765,7 +767,7 @@ class ChatController(args: Bundle) : require(files.isNotEmpty()) val data: Data = Data.Builder() .putStringArray(UploadAndShareFilesWorker.DEVICE_SOURCEFILES, files.toTypedArray()) - .putString(UploadAndShareFilesWorker.NC_TARGETPATH, conversationUser?.getAttachmentFolder()) + .putString(UploadAndShareFilesWorker.NC_TARGETPATH, CapabilitiesUtil.getAttachmentFolder(conversationUser)) .putString(UploadAndShareFilesWorker.ROOM_TOKEN, roomToken) .build() val uploadWorker: OneTimeWorkRequest = OneTimeWorkRequest.Builder(UploadAndShareFilesWorker::class.java) @@ -851,9 +853,8 @@ class ChatController(args: Bundle) : eventBus?.register(this) if (conversationUser?.userId != "?" && - conversationUser?.hasSpreedFeatureCapability("mention-flag") ?: false && - activity != null - ) { + CapabilitiesUtil.hasSpreedFeatureCapability(conversationUser, "mention-flag") ?: false && + activity != null) { activity?.findViewById(R.id.toolbar)?.setOnClickListener { v -> showConversationInfoScreen() } } @@ -1489,7 +1490,7 @@ class ChatController(args: Bundle) : override fun onPrepareOptionsMenu(menu: Menu) { super.onPrepareOptionsMenu(menu) conversationUser?.let { - if (it.hasSpreedFeatureCapability("read-only-rooms")) { + if (CapabilitiesUtil.hasSpreedFeatureCapability(it, "read-only-rooms")) { checkReadOnlyState() } } @@ -1813,7 +1814,7 @@ class ChatController(args: Bundle) : } if (!isUserAllowedByPrivileges) return false - if (!conversationUser.hasSpreedFeatureCapability("delete-messages")) return false + if (!CapabilitiesUtil.hasSpreedFeatureCapability(conversationUser, "delete-messages")) return false return true } diff --git a/app/src/main/java/com/nextcloud/talk/controllers/ContactsController.java b/app/src/main/java/com/nextcloud/talk/controllers/ContactsController.java index fd9d5243d..b4a231b9f 100644 --- a/app/src/main/java/com/nextcloud/talk/controllers/ContactsController.java +++ b/app/src/main/java/com/nextcloud/talk/controllers/ContactsController.java @@ -56,6 +56,7 @@ import com.nextcloud.talk.controllers.bottomsheet.OperationsMenuController; import com.nextcloud.talk.events.BottomSheetLockEvent; import com.nextcloud.talk.jobs.AddParticipantsToConversation; import com.nextcloud.talk.models.RetrofitBucket; +import com.nextcloud.talk.models.database.CapabilitiesUtil; import com.nextcloud.talk.models.database.UserEntity; import com.nextcloud.talk.models.json.autocomplete.AutocompleteOverall; import com.nextcloud.talk.models.json.autocomplete.AutocompleteUser; @@ -493,13 +494,13 @@ public class ContactsController extends BaseController implements SearchView.OnQ if (!isAddingParticipantsView) { // groups shareTypesList.add("1"); - } else if (currentUser.hasSpreedFeatureCapability("invite-groups-and-mails")) { + } else if (CapabilitiesUtil.hasSpreedFeatureCapability(currentUser, "invite-groups-and-mails")) { // groups shareTypesList.add("1"); // emails shareTypesList.add("4"); } - if (currentUser.hasSpreedFeatureCapability("circles-support")) { + if (CapabilitiesUtil.hasSpreedFeatureCapability(currentUser, "circles-support")) { // circles shareTypesList.add("7"); } @@ -974,8 +975,8 @@ public class ContactsController extends BaseController implements SearchView.OnQ } } - if (currentUser.hasSpreedFeatureCapability("last-room-activity") - && !currentUser.hasSpreedFeatureCapability("invite-groups-and-mails") && + if (CapabilitiesUtil.hasSpreedFeatureCapability(currentUser, "last-room-activity") + && !CapabilitiesUtil.hasSpreedFeatureCapability(currentUser, "invite-groups-and-mails") && "groups".equals(((UserItem) adapter.getItem(position)).getModel().getSource()) && participant.isSelected() && adapter.getSelectedItemCount() > 1) { diff --git a/app/src/main/java/com/nextcloud/talk/controllers/ConversationInfoController.kt b/app/src/main/java/com/nextcloud/talk/controllers/ConversationInfoController.kt index ddfb96798..2bffda65c 100644 --- a/app/src/main/java/com/nextcloud/talk/controllers/ConversationInfoController.kt +++ b/app/src/main/java/com/nextcloud/talk/controllers/ConversationInfoController.kt @@ -21,26 +21,19 @@ package com.nextcloud.talk.controllers import android.annotation.SuppressLint -import android.content.Context import android.graphics.drawable.Drawable import android.graphics.drawable.LayerDrawable import android.os.Bundle import android.text.TextUtils import android.util.Log -import android.view.LayoutInflater import android.view.MenuItem import android.view.View -import android.view.ViewGroup -import android.widget.ProgressBar import androidx.appcompat.widget.SwitchCompat -import androidx.emoji.widget.EmojiTextView -import androidx.recyclerview.widget.RecyclerView +import androidx.core.content.ContextCompat import androidx.work.Data import androidx.work.OneTimeWorkRequest import androidx.work.WorkManager import autodagger.AutoInjector -import butterknife.BindView -import butterknife.OnClick import com.afollestad.materialdialogs.LayoutMode.WRAP_CONTENT import com.afollestad.materialdialogs.MaterialDialog import com.afollestad.materialdialogs.bottomsheets.BottomSheet @@ -48,17 +41,19 @@ import com.afollestad.materialdialogs.datetime.dateTimePicker import com.bluelinelabs.conductor.RouterTransaction import com.bluelinelabs.conductor.changehandler.HorizontalChangeHandler import com.facebook.drawee.backends.pipeline.Fresco -import com.facebook.drawee.view.SimpleDraweeView import com.nextcloud.talk.R import com.nextcloud.talk.adapters.items.UserItem import com.nextcloud.talk.api.NcApi import com.nextcloud.talk.application.NextcloudTalkApplication -import com.nextcloud.talk.controllers.base.BaseController +import com.nextcloud.talk.controllers.base.NewBaseController import com.nextcloud.talk.controllers.bottomsheet.items.BasicListItemWithImage import com.nextcloud.talk.controllers.bottomsheet.items.listItemsWithImage +import com.nextcloud.talk.controllers.util.viewBinding +import com.nextcloud.talk.databinding.ControllerConversationInfoBinding import com.nextcloud.talk.events.EventStatus import com.nextcloud.talk.jobs.DeleteConversationWorker import com.nextcloud.talk.jobs.LeaveConversationWorker +import com.nextcloud.talk.models.database.CapabilitiesUtil import com.nextcloud.talk.models.database.UserEntity import com.nextcloud.talk.models.json.conversations.Conversation import com.nextcloud.talk.models.json.conversations.RoomOverall @@ -75,11 +70,6 @@ import com.nextcloud.talk.utils.bundle.BundleKeys import com.nextcloud.talk.utils.preferences.preferencestorage.DatabaseStorageModule import com.yarolegovich.lovelydialog.LovelySaveStateHandler import com.yarolegovich.lovelydialog.LovelyStandardDialog -import com.yarolegovich.mp.MaterialChoicePreference -import com.yarolegovich.mp.MaterialPreferenceCategory -import com.yarolegovich.mp.MaterialPreferenceScreen -import com.yarolegovich.mp.MaterialStandardPreference -import com.yarolegovich.mp.MaterialSwitchPreference import eu.davidea.flexibleadapter.FlexibleAdapter import eu.davidea.flexibleadapter.common.SmoothScrollLinearLayoutManager import io.reactivex.Observer @@ -96,70 +86,18 @@ import java.util.Locale import javax.inject.Inject @AutoInjector(NextcloudTalkApplication::class) -class ConversationInfoController(args: Bundle) : BaseController(args), FlexibleAdapter.OnItemClickListener { +class ConversationInfoController(args: Bundle) : NewBaseController(R.layout.controller_conversation_info, args), + FlexibleAdapter + .OnItemClickListener { + private val binding: ControllerConversationInfoBinding by viewBinding(ControllerConversationInfoBinding::bind) - @BindView(R.id.notification_settings) - lateinit var notificationsPreferenceScreen: MaterialPreferenceScreen + @Inject + @JvmField + var ncApi: NcApi? = null - @BindView(R.id.progressBar) - lateinit var progressBar: ProgressBar - - @BindView(R.id.conversation_info_message_notifications) - lateinit var messageNotificationLevel: MaterialChoicePreference - - @BindView(R.id.webinar_settings) - lateinit var conversationInfoWebinar: MaterialPreferenceScreen - - @BindView(R.id.conversation_info_lobby) - lateinit var conversationInfoLobby: MaterialSwitchPreference - - @BindView(R.id.conversation_info_name) - lateinit var nameCategoryView: MaterialPreferenceCategory - - @BindView(R.id.start_time_preferences) - lateinit var startTimeView: MaterialStandardPreference - - @BindView(R.id.avatar_image) - lateinit var conversationAvatarImageView: SimpleDraweeView - - @BindView(R.id.display_name_text) - lateinit var conversationDisplayName: EmojiTextView - - @BindView(R.id.conversation_description) - lateinit var descriptionCategoryView: MaterialPreferenceCategory - - @BindView(R.id.description_text) - lateinit var conversationDescription: EmojiTextView - - @BindView(R.id.participants_list_category) - lateinit var participantsListCategory: MaterialPreferenceCategory - - @BindView(R.id.addParticipantsAction) - lateinit var addParticipantsAction: MaterialStandardPreference - - @BindView(R.id.recycler_view) - lateinit var recyclerView: RecyclerView - - @BindView(R.id.deleteConversationAction) - lateinit var deleteConversationAction: MaterialStandardPreference - - @BindView(R.id.leaveConversationAction) - lateinit var leaveConversationAction: MaterialStandardPreference - - @BindView(R.id.ownOptions) - lateinit var ownOptionsCategory: MaterialPreferenceCategory - - @BindView(R.id.muteCalls) - lateinit var muteCalls: MaterialSwitchPreference - - @set:Inject - lateinit var ncApi: NcApi - - @set:Inject - lateinit var context: Context - - @set:Inject - lateinit var eventBus: EventBus + @Inject + @JvmField + var eventBus: EventBus? = null private val conversationToken: String? private val conversationUser: UserEntity? @@ -207,20 +145,20 @@ class ConversationInfoController(args: Bundle) : BaseController(args), FlexibleA } } - override fun inflateView(inflater: LayoutInflater, container: ViewGroup): View { - return inflater.inflate(R.layout.controller_conversation_info, container, false) - } - override fun onAttach(view: View) { super.onAttach(view) - eventBus.register(this) + eventBus?.register(this) if (databaseStorageModule == null) { databaseStorageModule = DatabaseStorageModule(conversationUser!!, conversationToken) } - notificationsPreferenceScreen.setStorageModule(databaseStorageModule) - conversationInfoWebinar.setStorageModule(databaseStorageModule) + binding.notificationSettingsView.notificationSettings.setStorageModule(databaseStorageModule) + binding.webinarInfoView.webinarSettings.setStorageModule(databaseStorageModule) + + binding.deleteConversationAction.setOnClickListener { showDeleteConversationDialog(null) } + binding.leaveConversationAction.setOnClickListener { leaveConversation() } + binding.addParticipantsAction.setOnClickListener { addParticipants() } fetchRoomInfo() } @@ -232,27 +170,27 @@ class ConversationInfoController(args: Bundle) : BaseController(args), FlexibleA saveStateHandler = LovelySaveStateHandler() } - addParticipantsAction.visibility = View.GONE + binding.addParticipantsAction.visibility = View.GONE } private fun setupWebinaryView() { - if (conversationUser!!.hasSpreedFeatureCapability("webinary-lobby") && + if (CapabilitiesUtil.hasSpreedFeatureCapability(conversationUser, "webinary-lobby") && ( conversation!!.type == Conversation.ConversationType.ROOM_GROUP_CALL || conversation!!.type == Conversation.ConversationType.ROOM_PUBLIC_CALL ) && conversation!!.canModerate(conversationUser) ) { - conversationInfoWebinar.visibility = View.VISIBLE + binding.webinarInfoView.webinarSettings.visibility = View.VISIBLE val isLobbyOpenToModeratorsOnly = conversation!!.lobbyState == Conversation.LobbyState.LOBBY_STATE_MODERATORS_ONLY - (conversationInfoLobby.findViewById(R.id.mp_checkable) as SwitchCompat) + (binding.webinarInfoView.conversationInfoLobby.findViewById(R.id.mp_checkable) as SwitchCompat) .isChecked = isLobbyOpenToModeratorsOnly reconfigureLobbyTimerView() - startTimeView.setOnClickListener { + binding.webinarInfoView.startTimePreferences.setOnClickListener { MaterialDialog(activity!!, BottomSheet(WRAP_CONTENT)).show { val currentTimeCalendar = Calendar.getInstance() if (conversation!!.lobbyTimer != null && conversation!!.lobbyTimer != 0L) { @@ -273,17 +211,18 @@ class ConversationInfoController(args: Bundle) : BaseController(args), FlexibleA } } - (conversationInfoLobby.findViewById(R.id.mp_checkable) as SwitchCompat).setOnCheckedChangeListener { _, _ -> + (binding.webinarInfoView.conversationInfoLobby.findViewById(R.id.mp_checkable) as SwitchCompat).setOnCheckedChangeListener { _, _ -> reconfigureLobbyTimerView() submitLobbyChanges() } } else { - conversationInfoWebinar.visibility = View.GONE + binding.webinarInfoView.webinarSettings.visibility = View.GONE } } fun reconfigureLobbyTimerView(dateTime: Calendar? = null) { - val isChecked = (conversationInfoLobby.findViewById(R.id.mp_checkable) as SwitchCompat).isChecked + val isChecked = + (binding.webinarInfoView.conversationInfoLobby.findViewById(R.id.mp_checkable) as SwitchCompat).isChecked if (dateTime != null && isChecked) { conversation!!.lobbyTimer = (dateTime.timeInMillis - (dateTime.time.seconds * 1000)) / 1000 @@ -295,34 +234,38 @@ class ConversationInfoController(args: Bundle) : BaseController(args), FlexibleA .LOBBY_STATE_MODERATORS_ONLY else Conversation.LobbyState.LOBBY_STATE_ALL_PARTICIPANTS if (conversation!!.lobbyTimer != null && conversation!!.lobbyTimer != java.lang.Long.MIN_VALUE && conversation!!.lobbyTimer != 0L) { - startTimeView.setSummary(DateUtils.getLocalDateStringFromTimestampForLobby(conversation!!.lobbyTimer)) + binding.webinarInfoView.startTimePreferences.setSummary( + DateUtils.getLocalDateStringFromTimestampForLobby( + conversation!!.lobbyTimer + ) + ) } else { - startTimeView.setSummary(R.string.nc_manual) + binding.webinarInfoView.startTimePreferences.setSummary(R.string.nc_manual) } if (isChecked) { - startTimeView.visibility = View.VISIBLE + binding.webinarInfoView.startTimePreferences.visibility = View.VISIBLE } else { - startTimeView.visibility = View.GONE + binding.webinarInfoView.startTimePreferences.visibility = View.GONE } } fun submitLobbyChanges() { val state = if ( - (conversationInfoLobby.findViewById(R.id.mp_checkable) as SwitchCompat).isChecked + (binding.webinarInfoView.conversationInfoLobby.findViewById(R.id.mp_checkable) as SwitchCompat).isChecked ) 1 else 0 val apiVersion = ApiUtils.getConversationApiVersion(conversationUser, intArrayOf(ApiUtils.APIv4, 1)) - ncApi.setLobbyForConversation( + ncApi?.setLobbyForConversation( ApiUtils.getCredentials(conversationUser!!.username, conversationUser.token), ApiUtils.getUrlForRoomWebinaryLobby(apiVersion, conversationUser.baseUrl, conversation!!.token), state, conversation!!.lobbyTimer ) - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .subscribe(object : Observer { + ?.subscribeOn(Schedulers.io()) + ?.observeOn(AndroidSchedulers.mainThread()) + ?.subscribe(object : Observer { override fun onComplete() { } @@ -352,7 +295,7 @@ class ConversationInfoController(args: Bundle) : BaseController(args), FlexibleA override fun onDetach(view: View) { super.onDetach(view) - eventBus.unregister(this) + eventBus?.unregister(this) } private fun showDeleteConversationDialog(savedInstanceState: Bundle?) { @@ -397,9 +340,9 @@ class ConversationInfoController(args: Bundle) : BaseController(args), FlexibleA } val layoutManager = SmoothScrollLinearLayoutManager(activity) - recyclerView.layoutManager = layoutManager - recyclerView.setHasFixedSize(true) - recyclerView.adapter = adapter + binding.recyclerView.layoutManager = layoutManager + binding.recyclerView.setHasFixedSize(true) + binding.recyclerView.adapter = adapter adapter!!.addListener(this) } @@ -438,17 +381,27 @@ class ConversationInfoController(args: Bundle) : BaseController(args), FlexibleA setupAdapter() - participantsListCategory.visibility = View.VISIBLE + binding.participantsListCategory.visibility = View.VISIBLE adapter!!.updateDataSet(recyclerViewItems) } + /** override fun getTitle(): String? { - return if (hasAvatarSpacing) { - " " + resources!!.getString(R.string.nc_conversation_menu_conversation_info) - } else { - resources!!.getString(R.string.nc_conversation_menu_conversation_info) - } + return if (hasAvatarSpacing) { + " " + resources!!.getString(R.string.nc_conversation_menu_conversation_info) + } else { + resources!!.getString(R.string.nc_conversation_menu_conversation_info) } + } + */ + + override val title: String? + get() = + if (hasAvatarSpacing) { + " " + resources!!.getString(R.string.nc_conversation_menu_conversation_info) + } else { + resources!!.getString(R.string.nc_conversation_menu_conversation_info) + } private fun getListOfParticipants() { var apiVersion = 1 @@ -457,13 +410,13 @@ class ConversationInfoController(args: Bundle) : BaseController(args), FlexibleA apiVersion = ApiUtils.getConversationApiVersion(conversationUser, intArrayOf(ApiUtils.APIv4, 1)) } - ncApi.getPeersForCall( + ncApi?.getPeersForCall( credentials, ApiUtils.getUrlForParticipants(apiVersion, conversationUser!!.baseUrl, conversationToken) ) - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .subscribe(object : Observer { + ?.subscribeOn(Schedulers.io()) + ?.observeOn(AndroidSchedulers.mainThread()) + ?.subscribe(object : Observer { override fun onSubscribe(d: Disposable) { participantsDisposable = d } @@ -481,7 +434,6 @@ class ConversationInfoController(args: Bundle) : BaseController(args), FlexibleA }) } - @OnClick(R.id.addParticipantsAction) internal fun addParticipants() { val bundle = Bundle() val existingParticipantsId = arrayListOf() @@ -511,8 +463,7 @@ class ConversationInfoController(args: Bundle) : BaseController(args), FlexibleA ) } - @OnClick(R.id.leaveConversationAction) - internal fun leaveConversation() { + private fun leaveConversation() { workerData?.let { WorkManager.getInstance().enqueue( OneTimeWorkRequest.Builder( @@ -535,11 +486,6 @@ class ConversationInfoController(args: Bundle) : BaseController(args), FlexibleA } } - @OnClick(R.id.deleteConversationAction) - internal fun deleteConversationClick() { - showDeleteConversationDialog(null) - } - private fun popTwoLastControllers() { var backstack = router.backstack backstack = backstack.subList(0, backstack.size - 2) @@ -553,10 +499,10 @@ class ConversationInfoController(args: Bundle) : BaseController(args), FlexibleA apiVersion = ApiUtils.getConversationApiVersion(conversationUser, intArrayOf(ApiUtils.APIv4, 1)) } - ncApi.getRoom(credentials, ApiUtils.getUrlForRoom(apiVersion, conversationUser!!.baseUrl, conversationToken)) - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .subscribe(object : Observer { + ncApi?.getRoom(credentials, ApiUtils.getUrlForRoom(apiVersion, conversationUser!!.baseUrl, conversationToken)) + ?.subscribeOn(Schedulers.io()) + ?.observeOn(AndroidSchedulers.mainThread()) + ?.subscribe(object : Observer { override fun onSubscribe(d: Disposable) { roomDisposable = d } @@ -567,49 +513,49 @@ class ConversationInfoController(args: Bundle) : BaseController(args), FlexibleA val conversationCopy = conversation if (conversationCopy!!.canModerate(conversationUser)) { - addParticipantsAction.visibility = View.VISIBLE + binding.addParticipantsAction.visibility = View.VISIBLE } else { - addParticipantsAction.visibility = View.GONE + binding.addParticipantsAction.visibility = View.GONE } if (isAttached && (!isBeingDestroyed || !isDestroyed)) { - ownOptionsCategory.visibility = View.VISIBLE + binding.ownOptions.visibility = View.VISIBLE setupWebinaryView() if (!conversation!!.canLeave(conversationUser)) { - leaveConversationAction.visibility = View.GONE + binding.leaveConversationAction.visibility = View.GONE } else { - leaveConversationAction.visibility = View.VISIBLE + binding.leaveConversationAction.visibility = View.VISIBLE } if (!conversation!!.canDelete(conversationUser)) { - deleteConversationAction.visibility = View.GONE + binding.deleteConversationAction.visibility = View.GONE } else { - deleteConversationAction.visibility = View.VISIBLE + binding.deleteConversationAction.visibility = View.VISIBLE } if (Conversation.ConversationType.ROOM_SYSTEM == conversation!!.type) { - muteCalls.visibility = View.GONE + binding.notificationSettingsView.muteCalls.visibility = View.GONE } getListOfParticipants() - progressBar.visibility = View.GONE + binding.progressBar.visibility = View.GONE - nameCategoryView.visibility = View.VISIBLE + binding.conversationInfoName.visibility = View.VISIBLE - conversationDisplayName.text = conversation!!.displayName + binding.displayNameText.text = conversation!!.displayName if (conversation!!.description != null && !conversation!!.description.isEmpty()) { - conversationDescription.text = conversation!!.description - descriptionCategoryView.visibility = View.VISIBLE + binding.descriptionText.text = conversation!!.description + binding.conversationDescription.visibility = View.VISIBLE } loadConversationAvatar() adjustNotificationLevelUI() - notificationsPreferenceScreen.visibility = View.VISIBLE + binding.notificationSettingsView.notificationSettings.visibility = View.VISIBLE } } @@ -624,9 +570,9 @@ class ConversationInfoController(args: Bundle) : BaseController(args), FlexibleA private fun adjustNotificationLevelUI() { if (conversation != null) { - if (conversationUser != null && conversationUser.hasSpreedFeatureCapability("notification-levels")) { - messageNotificationLevel.isEnabled = true - messageNotificationLevel.alpha = 1.0f + if (conversationUser != null && CapabilitiesUtil.hasSpreedFeatureCapability(conversationUser, "notification-levels")) { + binding.notificationSettingsView.conversationInfoMessageNotifications.isEnabled = true + binding.notificationSettingsView.conversationInfoMessageNotifications.alpha = 1.0f if (conversation!!.notificationLevel != Conversation.NotificationLevel.DEFAULT) { val stringValue: String = @@ -637,13 +583,13 @@ class ConversationInfoController(args: Bundle) : BaseController(args), FlexibleA else -> "mention" } - messageNotificationLevel.value = stringValue + binding.notificationSettingsView.conversationInfoMessageNotifications.value = stringValue } else { setProperNotificationValue(conversation) } } else { - messageNotificationLevel.isEnabled = false - messageNotificationLevel.alpha = 0.38f + binding.notificationSettingsView.conversationInfoMessageNotifications.isEnabled = false + binding.notificationSettingsView.conversationInfoMessageNotifications.alpha = 0.38f setProperNotificationValue(conversation) } } @@ -652,13 +598,13 @@ class ConversationInfoController(args: Bundle) : BaseController(args), FlexibleA private fun setProperNotificationValue(conversation: Conversation?) { if (conversation!!.type == Conversation.ConversationType.ROOM_TYPE_ONE_TO_ONE_CALL) { // hack to see if we get mentioned always or just on mention - if (conversationUser!!.hasSpreedFeatureCapability("mention-flag")) { - messageNotificationLevel.value = "always" + if (CapabilitiesUtil.hasSpreedFeatureCapability(conversationUser, "mention-flag")) { + binding.notificationSettingsView.conversationInfoMessageNotifications.value = "always" } else { - messageNotificationLevel.value = "mention" + binding.notificationSettingsView.conversationInfoMessageNotifications.value = "mention" } } else { - messageNotificationLevel.value = "mention" + binding.notificationSettingsView.conversationInfoMessageNotifications.value = "mention" } } @@ -666,7 +612,7 @@ class ConversationInfoController(args: Bundle) : BaseController(args), FlexibleA when (conversation!!.type) { Conversation.ConversationType.ROOM_TYPE_ONE_TO_ONE_CALL -> if (!TextUtils.isEmpty(conversation!!.name)) { val draweeController = Fresco.newDraweeControllerBuilder() - .setOldController(conversationAvatarImageView.controller) + .setOldController(binding.avatarImage.controller) .setAutoPlayAnimations(true) .setImageRequest( DisplayUtils.getImageRequestForUrl( @@ -678,20 +624,20 @@ class ConversationInfoController(args: Bundle) : BaseController(args), FlexibleA ) ) .build() - conversationAvatarImageView.controller = draweeController + binding.avatarImage.controller = draweeController } - Conversation.ConversationType.ROOM_GROUP_CALL -> conversationAvatarImageView.hierarchy.setPlaceholderImage( + Conversation.ConversationType.ROOM_GROUP_CALL -> binding.avatarImage.hierarchy.setPlaceholderImage( R.drawable.ic_circular_group ) - Conversation.ConversationType.ROOM_PUBLIC_CALL -> conversationAvatarImageView.hierarchy.setPlaceholderImage( + Conversation.ConversationType.ROOM_PUBLIC_CALL -> binding.avatarImage.hierarchy.setPlaceholderImage( R.drawable.ic_circular_link ) Conversation.ConversationType.ROOM_SYSTEM -> { val layers = arrayOfNulls(2) - layers[0] = context.getDrawable(R.drawable.ic_launcher_background) - layers[1] = context.getDrawable(R.drawable.ic_launcher_foreground) + layers[0] = ContextCompat.getDrawable(context!!, R.drawable.ic_launcher_background) + layers[1] = ContextCompat.getDrawable(context!!, R.drawable.ic_launcher_foreground) val layerDrawable = LayerDrawable(layers) - conversationAvatarImageView.hierarchy.setPlaceholderImage(DisplayUtils.getRoundedDrawable(layerDrawable)) + binding.avatarImage.hierarchy.setPlaceholderImage(DisplayUtils.getRoundedDrawable(layerDrawable)) } else -> { @@ -720,7 +666,7 @@ class ConversationInfoController(args: Bundle) : BaseController(args), FlexibleA if (participant.type == Participant.ParticipantType.MODERATOR || participant.type == Participant.ParticipantType.GUEST_MODERATOR ) { - ncApi.demoteAttendeeFromModerator( + ncApi?.demoteAttendeeFromModerator( credentials, ApiUtils.getUrlForRoomModerators( apiVersion, @@ -729,13 +675,13 @@ class ConversationInfoController(args: Bundle) : BaseController(args), FlexibleA ), participant.attendeeId ) - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .subscribe(subscriber) + ?.subscribeOn(Schedulers.io()) + ?.observeOn(AndroidSchedulers.mainThread()) + ?.subscribe(subscriber) } else if (participant.type == Participant.ParticipantType.USER || participant.type == Participant.ParticipantType.GUEST ) { - ncApi.promoteAttendeeToModerator( + ncApi?.promoteAttendeeToModerator( credentials, ApiUtils.getUrlForRoomModerators( apiVersion, @@ -744,9 +690,9 @@ class ConversationInfoController(args: Bundle) : BaseController(args), FlexibleA ), participant.attendeeId ) - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .subscribe(subscriber) + ?.subscribeOn(Schedulers.io()) + ?.observeOn(AndroidSchedulers.mainThread()) + ?.subscribe(subscriber) } } @@ -769,7 +715,7 @@ class ConversationInfoController(args: Bundle) : BaseController(args), FlexibleA } if (participant.type == Participant.ParticipantType.MODERATOR) { - ncApi.demoteModeratorToUser( + ncApi?.demoteModeratorToUser( credentials, ApiUtils.getUrlForRoomModerators( apiVersion, @@ -778,11 +724,11 @@ class ConversationInfoController(args: Bundle) : BaseController(args), FlexibleA ), participant.userId ) - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .subscribe(subscriber) + ?.subscribeOn(Schedulers.io()) + ?.observeOn(AndroidSchedulers.mainThread()) + ?.subscribe(subscriber) } else if (participant.type == Participant.ParticipantType.USER) { - ncApi.promoteUserToModerator( + ncApi?.promoteUserToModerator( credentials, ApiUtils.getUrlForRoomModerators( apiVersion, @@ -791,15 +737,15 @@ class ConversationInfoController(args: Bundle) : BaseController(args), FlexibleA ), participant.userId ) - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .subscribe(subscriber) + ?.subscribeOn(Schedulers.io()) + ?.observeOn(AndroidSchedulers.mainThread()) + ?.subscribe(subscriber) } } fun removeAttendeeFromConversation(apiVersion: Int, participant: Participant) { if (apiVersion >= ApiUtils.APIv4) { - ncApi.removeAttendeeFromConversation( + ncApi?.removeAttendeeFromConversation( credentials, ApiUtils.getUrlForAttendees( apiVersion, @@ -808,9 +754,9 @@ class ConversationInfoController(args: Bundle) : BaseController(args), FlexibleA ), participant.attendeeId ) - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .subscribe(object : Observer { + ?.subscribeOn(Schedulers.io()) + ?.observeOn(AndroidSchedulers.mainThread()) + ?.subscribe(object : Observer { override fun onSubscribe(d: Disposable) { } @@ -830,7 +776,7 @@ class ConversationInfoController(args: Bundle) : BaseController(args), FlexibleA if (participant.type == Participant.ParticipantType.GUEST || participant.type == Participant.ParticipantType.USER_FOLLOWING_LINK ) { - ncApi.removeParticipantFromConversation( + ncApi?.removeParticipantFromConversation( credentials, ApiUtils.getUrlForRemovingParticipantFromConversation( conversationUser!!.baseUrl, @@ -839,9 +785,9 @@ class ConversationInfoController(args: Bundle) : BaseController(args), FlexibleA ), participant.sessionId ) - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .subscribe(object : Observer { + ?.subscribeOn(Schedulers.io()) + ?.observeOn(AndroidSchedulers.mainThread()) + ?.subscribe(object : Observer { override fun onSubscribe(d: Disposable) { } @@ -858,7 +804,7 @@ class ConversationInfoController(args: Bundle) : BaseController(args), FlexibleA } }) } else { - ncApi.removeParticipantFromConversation( + ncApi?.removeParticipantFromConversation( credentials, ApiUtils.getUrlForRemovingParticipantFromConversation( conversationUser!!.baseUrl, @@ -867,9 +813,9 @@ class ConversationInfoController(args: Bundle) : BaseController(args), FlexibleA ), participant.userId ) - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .subscribe(object : Observer { + ?.subscribeOn(Schedulers.io()) + ?.observeOn(AndroidSchedulers.mainThread()) + ?.subscribe(object : Observer { override fun onSubscribe(d: Disposable) { } @@ -904,7 +850,7 @@ class ConversationInfoController(args: Bundle) : BaseController(args), FlexibleA val items = mutableListOf( BasicListItemWithImage( R.drawable.ic_lock_grey600_24px, - context.getString(R.string.nc_attendee_pin, participant.attendeePin) + context!!.getString(R.string.nc_attendee_pin, participant.attendeePin) ) ) MaterialDialog(activity!!, BottomSheet(WRAP_CONTENT)).show { @@ -930,7 +876,7 @@ class ConversationInfoController(args: Bundle) : BaseController(args), FlexibleA val items = mutableListOf( BasicListItemWithImage( R.drawable.ic_delete_grey600_24dp, - context.getString(R.string.nc_remove_group_and_members) + context!!.getString(R.string.nc_remove_group_and_members) ) ) MaterialDialog(activity!!, BottomSheet(WRAP_CONTENT)).show { @@ -946,16 +892,16 @@ class ConversationInfoController(args: Bundle) : BaseController(args), FlexibleA return true } - var items = mutableListOf( + val items = mutableListOf( BasicListItemWithImage( R.drawable.ic_lock_grey600_24px, - context.getString(R.string.nc_attendee_pin, participant.attendeePin) + context!!.getString(R.string.nc_attendee_pin, participant.attendeePin) ), - BasicListItemWithImage(R.drawable.ic_pencil_grey600_24dp, context.getString(R.string.nc_promote)), - BasicListItemWithImage(R.drawable.ic_pencil_grey600_24dp, context.getString(R.string.nc_demote)), + BasicListItemWithImage(R.drawable.ic_pencil_grey600_24dp, context!!.getString(R.string.nc_promote)), + BasicListItemWithImage(R.drawable.ic_pencil_grey600_24dp, context!!.getString(R.string.nc_demote)), BasicListItemWithImage( R.drawable.ic_delete_grey600_24dp, - context.getString(R.string.nc_remove_participant) + context!!.getString(R.string.nc_remove_participant) ) ) @@ -1025,7 +971,11 @@ class ConversationInfoController(args: Bundle) : BaseController(args), FlexibleA val rightIsGroup = right.model.actorType == GROUPS if (leftIsGroup != rightIsGroup) { // Groups below participants - return if (rightIsGroup) { -1 } else { 1 } + return if (rightIsGroup) { + -1 + } else { + 1 + } } if (left.isOnline && !right.isOnline) { diff --git a/app/src/main/java/com/nextcloud/talk/controllers/ConversationsListController.java b/app/src/main/java/com/nextcloud/talk/controllers/ConversationsListController.java index 744af7aff..a3af0bc69 100644 --- a/app/src/main/java/com/nextcloud/talk/controllers/ConversationsListController.java +++ b/app/src/main/java/com/nextcloud/talk/controllers/ConversationsListController.java @@ -79,6 +79,7 @@ import com.nextcloud.talk.jobs.AccountRemovalWorker; import com.nextcloud.talk.jobs.ContactAddressBookWorker; import com.nextcloud.talk.jobs.DeleteConversationWorker; import com.nextcloud.talk.jobs.UploadAndShareFilesWorker; +import com.nextcloud.talk.models.database.CapabilitiesUtil; import com.nextcloud.talk.models.database.UserEntity; import com.nextcloud.talk.models.json.conversations.Conversation; import com.nextcloud.talk.models.json.participants.Participant; @@ -280,13 +281,14 @@ public class ConversationsListController extends BaseController implements Searc currentUser = userUtils.getCurrentUser(); if (currentUser != null) { - if (currentUser.isServerEOL()) { + if (CapabilitiesUtil.isServerEOL(currentUser)) { showServerEOLDialog(); return; } credentials = ApiUtils.getCredentials(currentUser.getUsername(), currentUser.getToken()); - shouldUseLastMessageLayout = currentUser.hasSpreedFeatureCapability("last-room-activity"); + shouldUseLastMessageLayout = CapabilitiesUtil.hasSpreedFeatureCapability(currentUser, + "last-room-activity"); if (getActivity() != null && getActivity() instanceof MainActivity) { loadUserAvatar(((MainActivity) getActivity()).binding.switchAccountButton); } @@ -489,7 +491,7 @@ public class ConversationsListController extends BaseController implements Searc } } - if (currentUser.hasSpreedFeatureCapability("last-room-activity")) { + if (CapabilitiesUtil.hasSpreedFeatureCapability(currentUser, "last-room-activity")) { Collections.sort(callItems, (o1, o2) -> { Conversation conversation1 = ((ConversationItem) o1).getModel(); Conversation conversation2 = ((ConversationItem) o2).getModel(); @@ -817,7 +819,7 @@ public class ConversationsListController extends BaseController implements Searc if (showShareToScreen) { Log.d(TAG, "sharing to multiple rooms not yet implemented. onItemLongClick is ignored."); - } else if (currentUser.hasSpreedFeatureCapability("last-room-activity")) { + } else if (CapabilitiesUtil.hasSpreedFeatureCapability(currentUser, "last-room-activity")) { Object clickedItem = adapter.getItem(position); if (clickedItem != null) { Conversation conversation; diff --git a/app/src/main/java/com/nextcloud/talk/controllers/ProfileController.java b/app/src/main/java/com/nextcloud/talk/controllers/ProfileController.java index f4070d2b5..1d146450d 100644 --- a/app/src/main/java/com/nextcloud/talk/controllers/ProfileController.java +++ b/app/src/main/java/com/nextcloud/talk/controllers/ProfileController.java @@ -53,6 +53,7 @@ import com.nextcloud.talk.application.NextcloudTalkApplication; import com.nextcloud.talk.components.filebrowser.controllers.BrowserController; import com.nextcloud.talk.components.filebrowser.controllers.BrowserForAvatarController; import com.nextcloud.talk.controllers.base.BaseController; +import com.nextcloud.talk.models.database.CapabilitiesUtil; import com.nextcloud.talk.models.database.UserEntity; import com.nextcloud.talk.models.json.generic.GenericOverall; import com.nextcloud.talk.models.json.userprofile.Scope; @@ -170,7 +171,7 @@ public class ProfileController extends BaseController { getActivity().findViewById(R.id.emptyList).setVisibility(View.GONE); getActivity().findViewById(R.id.userinfo_list).setVisibility(View.VISIBLE); - if (currentUser.isAvatarEndpointAvailable()) { + if (CapabilitiesUtil.isAvatarEndpointAvailable(currentUser)) { // TODO later avatar can also be checked via user fields, for now it is in Talk capability getActivity().findViewById(R.id.avatar_buttons).setVisibility(View.VISIBLE); } @@ -345,7 +346,7 @@ public class ProfileController extends BaseController { } // show edit button - if (currentUser.canEditScopes()) { + if (CapabilitiesUtil.canEditScopes(currentUser)) { ncApi.getEditableUserProfileFields(ApiUtils.getCredentials(currentUser.getUsername(), currentUser.getToken()), ApiUtils.getUrlForUserFields(currentUser.getBaseUrl())) .subscribeOn(Schedulers.io()) diff --git a/app/src/main/java/com/nextcloud/talk/controllers/SettingsController.java b/app/src/main/java/com/nextcloud/talk/controllers/SettingsController.java index 3355b9549..3dfa341ea 100644 --- a/app/src/main/java/com/nextcloud/talk/controllers/SettingsController.java +++ b/app/src/main/java/com/nextcloud/talk/controllers/SettingsController.java @@ -68,6 +68,7 @@ import com.nextcloud.talk.controllers.base.BaseController; import com.nextcloud.talk.jobs.AccountRemovalWorker; import com.nextcloud.talk.jobs.ContactAddressBookWorker; import com.nextcloud.talk.models.RingtoneSettings; +import com.nextcloud.talk.models.database.CapabilitiesUtil; import com.nextcloud.talk.models.database.UserEntity; import com.nextcloud.talk.models.json.generic.GenericOverall; import com.nextcloud.talk.models.json.userprofile.UserProfileOverall; @@ -317,7 +318,7 @@ public class SettingsController extends BaseController { .popChangeHandler(new HorizontalChangeHandler())); }); - if (userUtils.getCurrentUser().isPhoneBookIntegrationAvailable()) { + if (CapabilitiesUtil.isPhoneBookIntegrationAvailable(userUtils.getCurrentUser())) { phoneBookIntegrationPreference.setVisibility(View.VISIBLE); } else { phoneBookIntegrationPreference.setVisibility(View.GONE); @@ -456,8 +457,8 @@ public class SettingsController extends BaseController { ((Checkable) incognitoKeyboardSwitchPreference.findViewById(R.id.mp_checkable)).setChecked(appPreferences.getIsKeyboardIncognito()); } - if (userUtils.getCurrentUser().isReadStatusAvailable()) { - ((Checkable) readPrivacyPreference.findViewById(R.id.mp_checkable)).setChecked(!currentUser.isReadStatusPrivate()); + if (CapabilitiesUtil.isReadStatusAvailable(userUtils.getCurrentUser())) { + ((Checkable) readPrivacyPreference.findViewById(R.id.mp_checkable)).setChecked(!CapabilitiesUtil.isReadStatusPrivate(currentUser)); } else { readPrivacyPreference.setVisibility(View.GONE); } @@ -537,12 +538,12 @@ public class SettingsController extends BaseController { baseUrlTextView.setText(Uri.parse(currentUser.getBaseUrl()).getHost()); - if (currentUser.isServerEOL()) { + if (CapabilitiesUtil.isServerEOL(currentUser)) { serverAgeTextView.setTextColor(ContextCompat.getColor(context, R.color.nc_darkRed)); serverAgeTextView.setText(R.string.nc_settings_server_eol); serverAgeIcon.setColorFilter(ContextCompat.getColor(context, R.color.nc_darkRed), PorterDuff.Mode.SRC_IN); - } else if (currentUser.isServerAlmostEOL()) { + } else if (CapabilitiesUtil.isServerAlmostEOL(currentUser)) { serverAgeTextView.setTextColor(ContextCompat.getColor(context, R.color.nc_darkYellow)); serverAgeTextView.setText(R.string.nc_settings_server_almost_eol); serverAgeIcon.setColorFilter(ContextCompat.getColor(context, R.color.nc_darkYellow), diff --git a/app/src/main/java/com/nextcloud/talk/controllers/base/NewBaseController.kt b/app/src/main/java/com/nextcloud/talk/controllers/base/NewBaseController.kt new file mode 100644 index 000000000..16d3f922d --- /dev/null +++ b/app/src/main/java/com/nextcloud/talk/controllers/base/NewBaseController.kt @@ -0,0 +1,259 @@ +/* + * Nextcloud Talk application + * + * @author Andy Scherzinger + * @author BlueLine Labs, Inc. + * @author Mario Danic + * Copyright (C) 2021 Andy Scherzinger (info@andy-scherzinger.de) + * Copyright (C) 2020 Mario Danic (mario@lovelyhq.com) + * Copyright (C) 2016 BlueLine Labs, Inc. + *

+ * 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. + */ +package com.nextcloud.talk.controllers.base + +import android.animation.AnimatorInflater +import android.content.Context +import android.os.Build +import android.os.Bundle +import android.util.Log +import android.view.LayoutInflater +import android.view.MenuItem +import android.view.View +import android.view.ViewGroup +import android.view.inputmethod.EditorInfo +import android.view.inputmethod.InputMethodManager +import android.widget.EditText +import androidx.annotation.LayoutRes +import androidx.annotation.RequiresApi +import androidx.appcompat.app.ActionBar +import androidx.core.content.res.ResourcesCompat +import autodagger.AutoInjector +import com.bluelinelabs.conductor.Controller +import com.bluelinelabs.conductor.ControllerChangeHandler +import com.bluelinelabs.conductor.ControllerChangeType +import com.google.android.material.appbar.AppBarLayout +import com.nextcloud.talk.R +import com.nextcloud.talk.activities.MainActivity +import com.nextcloud.talk.application.NextcloudTalkApplication +import com.nextcloud.talk.application.NextcloudTalkApplication.Companion.sharedApplication +import com.nextcloud.talk.controllers.AccountVerificationController +import com.nextcloud.talk.controllers.ServerSelectionController +import com.nextcloud.talk.controllers.SwitchAccountController +import com.nextcloud.talk.controllers.WebViewLoginController +import com.nextcloud.talk.controllers.base.providers.ActionBarProvider +import com.nextcloud.talk.utils.DisplayUtils +import com.nextcloud.talk.utils.preferences.AppPreferences +import java.util.ArrayList +import javax.inject.Inject +import kotlin.jvm.internal.Intrinsics + +@AutoInjector(NextcloudTalkApplication::class) +abstract class NewBaseController(@LayoutRes var layoutRes: Int, args: Bundle? = null) : Controller(args) { + enum class AppBarLayoutType { + TOOLBAR, SEARCH_BAR, EMPTY + } + + @Inject + @JvmField + var appPreferences: AppPreferences? = null + + @Inject + @JvmField + var context: Context? = null + + protected open val title: String? + get() = null + + protected val actionBar: ActionBar? + get() { + var actionBarProvider: ActionBarProvider? = null + if (this.activity is ActionBarProvider) { + try { + actionBarProvider = this.activity as ActionBarProvider? + } catch (e: Exception) { + Log.d(TAG, "Failed to fetch the action bar provider", e) + } + } + return actionBarProvider?.supportActionBar + } + + init { + addLifecycleListener(object : LifecycleListener() { + override fun postCreateView(controller: Controller, view: View) { + onViewBound(view) + actionBar?.let { setTitle() } + } + }) + cleanTempCertPreference() + } + + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup, + savedViewState: Bundle? + ): View { + return inflater.inflate(layoutRes, container, false) + } + + protected open fun onViewBound(view: View) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && appPreferences!!.isKeyboardIncognito) { + disableKeyboardPersonalisedLearning(view as ViewGroup) + if (activity != null && activity is MainActivity) { + val activity = activity as MainActivity? + disableKeyboardPersonalisedLearning(activity!!.binding.appBar) + } + } + } + + override fun onAttach(view: View) { + showSearchOrToolbar() + setTitle() + if (actionBar != null) { + actionBar!!.setDisplayHomeAsUpEnabled(parentController != null || router.backstackSize > 1) + } + super.onAttach(view) + } + + protected fun showSearchOrToolbar() { + if (activity != null && activity is MainActivity) { + val showSearchBar = appBarLayoutType == AppBarLayoutType.SEARCH_BAR + val activity = activity as MainActivity? + if (appBarLayoutType == AppBarLayoutType.EMPTY) { + activity!!.binding.toolbar.visibility = View.GONE + activity.binding.searchToolbar.visibility = View.GONE + } else { + val layoutParams = activity!!.binding.searchToolbar.layoutParams as AppBarLayout.LayoutParams + if (showSearchBar) { + activity.binding.searchToolbar.visibility = View.VISIBLE + activity.binding.searchText.hint = searchHint + activity.binding.toolbar.visibility = View.GONE + //layoutParams.setScrollFlags(AppBarLayout.LayoutParams.SCROLL_FLAG_SCROLL | AppBarLayout.LayoutParams.SCROLL_FLAG_SNAP | AppBarLayout.LayoutParams.SCROLL_FLAG_ENTER_ALWAYS); + layoutParams.scrollFlags = 0 + activity.binding.appBar.stateListAnimator = AnimatorInflater.loadStateListAnimator( + activity.binding.appBar.context, + R.animator.appbar_elevation_off + ) + } else { + activity.binding.searchToolbar.visibility = View.GONE + activity.binding.toolbar.visibility = View.VISIBLE + layoutParams.scrollFlags = 0 + activity.binding.appBar.stateListAnimator = AnimatorInflater.loadStateListAnimator( + activity.binding.appBar.context, + R.animator.appbar_elevation_on + ) + } + activity.binding.searchToolbar.layoutParams = layoutParams + if (resources != null) { + if (showSearchBar) { + DisplayUtils.applyColorToStatusBar( + activity, ResourcesCompat.getColor( + resources!!, + R.color.bg_default, null + ) + ) + } else { + DisplayUtils.applyColorToStatusBar( + activity, ResourcesCompat.getColor( + resources!!, + R.color.appbar, null + ) + ) + } + } + } + if (resources != null) { + DisplayUtils.applyColorToNavigationBar( + activity.window, + ResourcesCompat.getColor(resources!!, R.color.bg_default, null) + ) + } + } + } + + override fun onDetach(view: View) { + super.onDetach(view) + val imm = context!!.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager + imm.hideSoftInputFromWindow(view.windowToken, 0) + } + + protected fun setTitle() { + if (title != null && actionBar != null) { + run { + var parentController = parentController + while (parentController != null) { + if (parentController is BaseController && parentController.title != null) { + return + } + parentController = parentController.parentController + } + } + actionBar!!.title = title + } + } + + override fun onOptionsItemSelected(item: MenuItem): Boolean { + if (item.itemId == android.R.id.home) { + router.popCurrentController() + return true + } + return super.onOptionsItemSelected(item) + } + + override fun onChangeStarted(changeHandler: ControllerChangeHandler, changeType: ControllerChangeType) { + super.onChangeStarted(changeHandler, changeType) + if (changeType.isEnter && actionBar != null) { + configureMenu(actionBar!!) + } + } + + fun configureMenu(toolbar: ActionBar) { + Intrinsics.checkNotNullParameter(toolbar, "toolbar") + } + + private fun cleanTempCertPreference() { + sharedApplication!!.componentApplication.inject(this) + val temporaryClassNames: MutableList = ArrayList() + temporaryClassNames.add(ServerSelectionController::class.java.name) + temporaryClassNames.add(AccountVerificationController::class.java.name) + temporaryClassNames.add(WebViewLoginController::class.java.name) + temporaryClassNames.add(SwitchAccountController::class.java.name) + if (!temporaryClassNames.contains(javaClass.name)) { + appPreferences!!.removeTemporaryClientCertAlias() + } + } + + @RequiresApi(api = Build.VERSION_CODES.O) + private fun disableKeyboardPersonalisedLearning(viewGroup: ViewGroup) { + var view: View? + var editText: EditText + for (i in 0 until viewGroup.childCount) { + view = viewGroup.getChildAt(i) + if (view is EditText) { + editText = view + editText.imeOptions = editText.imeOptions or EditorInfo.IME_FLAG_NO_PERSONALIZED_LEARNING + } else if (view is ViewGroup) { + disableKeyboardPersonalisedLearning(view) + } + } + } + + val appBarLayoutType: AppBarLayoutType + get() = AppBarLayoutType.TOOLBAR + val searchHint: String + get() = context!!.getString(R.string.appbar_search_in, context!!.getString(R.string.nc_app_name)) + + companion object { + private val TAG = BaseController::class.java.simpleName + } +} \ No newline at end of file diff --git a/app/src/main/java/com/nextcloud/talk/controllers/bottomsheet/CallMenuController.java b/app/src/main/java/com/nextcloud/talk/controllers/bottomsheet/CallMenuController.java index 1925c4d89..3dcba6705 100644 --- a/app/src/main/java/com/nextcloud/talk/controllers/bottomsheet/CallMenuController.java +++ b/app/src/main/java/com/nextcloud/talk/controllers/bottomsheet/CallMenuController.java @@ -48,6 +48,7 @@ import com.nextcloud.talk.controllers.base.BaseController; import com.nextcloud.talk.events.BottomSheetLockEvent; import com.nextcloud.talk.interfaces.ConversationMenuInterface; import com.nextcloud.talk.jobs.LeaveConversationWorker; +import com.nextcloud.talk.models.database.CapabilitiesUtil; import com.nextcloud.talk.models.database.UserEntity; import com.nextcloud.talk.models.json.conversations.Conversation; import com.nextcloud.talk.utils.DisplayUtils; @@ -151,7 +152,7 @@ public class CallMenuController extends BaseController implements FlexibleAdapte if (conversation.isFavorite()) { menuItems.add(new MenuItem(getResources().getString(R.string.nc_remove_from_favorites), 97, DisplayUtils.getTintedDrawable(getResources(), R.drawable.ic_star_border_black_24dp, R.color.grey_600))); - } else if (currentUser.hasSpreedFeatureCapability("favorites")) { + } else if (CapabilitiesUtil.hasSpreedFeatureCapability(currentUser, "favorites")) { menuItems.add(new MenuItem(getResources().getString(R.string.nc_add_to_favorites) , 98, DisplayUtils.getTintedDrawable(getResources(), R.drawable.ic_star_black_24dp, R.color.grey_600))); } diff --git a/app/src/main/java/com/nextcloud/talk/controllers/bottomsheet/OperationsMenuController.java b/app/src/main/java/com/nextcloud/talk/controllers/bottomsheet/OperationsMenuController.java index d14cd85e2..5982902cd 100644 --- a/app/src/main/java/com/nextcloud/talk/controllers/bottomsheet/OperationsMenuController.java +++ b/app/src/main/java/com/nextcloud/talk/controllers/bottomsheet/OperationsMenuController.java @@ -34,8 +34,6 @@ import android.widget.ImageView; import android.widget.ProgressBar; import android.widget.TextView; -import androidx.annotation.NonNull; - import com.bluelinelabs.conductor.RouterTransaction; import com.bluelinelabs.conductor.changehandler.HorizontalChangeHandler; import com.bluelinelabs.logansquare.LoganSquare; @@ -46,6 +44,7 @@ import com.nextcloud.talk.application.NextcloudTalkApplication; import com.nextcloud.talk.controllers.base.BaseController; import com.nextcloud.talk.events.BottomSheetLockEvent; import com.nextcloud.talk.models.RetrofitBucket; +import com.nextcloud.talk.models.database.CapabilitiesUtil; import com.nextcloud.talk.models.database.UserEntity; import com.nextcloud.talk.models.json.capabilities.Capabilities; import com.nextcloud.talk.models.json.capabilities.CapabilitiesOverall; @@ -69,6 +68,7 @@ import java.util.ArrayList; import javax.inject.Inject; +import androidx.annotation.NonNull; import autodagger.AutoInjector; import butterknife.BindView; import io.reactivex.Observer; @@ -581,8 +581,10 @@ public class OperationsMenuController extends BaseController { int apiVersion = ApiUtils.getConversationApiVersion(currentUser, new int[] {4, 1}); - if (localInvitedUsers.size() > 0 || (localInvitedGroups.size() > 0 && currentUser.hasSpreedFeatureCapability("invite-groups-and-mails"))) { - if ((localInvitedGroups.size() > 0 && currentUser.hasSpreedFeatureCapability("invite-groups-and-mails"))) { + if (localInvitedUsers.size() > 0 || (localInvitedGroups.size() > 0 && + CapabilitiesUtil.hasSpreedFeatureCapability(currentUser, "invite-groups-and-mails"))) { + if ((localInvitedGroups.size() > 0 && + CapabilitiesUtil.hasSpreedFeatureCapability(currentUser, "invite-groups-and-mails"))) { for (int i = 0; i < localInvitedGroups.size(); i++) { final String groupId = localInvitedGroups.get(i); retrofitBucket = ApiUtils.getRetrofitBucketForAddParticipantWithSource( diff --git a/app/src/main/java/com/nextcloud/talk/controllers/util/ControllerViewBindingDelegate.kt b/app/src/main/java/com/nextcloud/talk/controllers/util/ControllerViewBindingDelegate.kt new file mode 100644 index 000000000..5a15eb9ed --- /dev/null +++ b/app/src/main/java/com/nextcloud/talk/controllers/util/ControllerViewBindingDelegate.kt @@ -0,0 +1,49 @@ +/* + * Nextcloud Talk application + * + * @author BlueLine Labs, Inc. + * Copyright (C) 2016 BlueLine Labs, Inc. + *

+ * 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. + */ +package com.nextcloud.talk.controllers.util + +import android.view.View +import androidx.lifecycle.LifecycleObserver +import androidx.viewbinding.ViewBinding +import com.bluelinelabs.conductor.Controller +import kotlin.properties.ReadOnlyProperty +import kotlin.reflect.KProperty + +fun Controller.viewBinding(bindingFactory: (View) -> T) = + ControllerViewBindingDelegate(this, bindingFactory) + +class ControllerViewBindingDelegate( + controller: Controller, + private val viewBinder: (View) -> T +) : ReadOnlyProperty, LifecycleObserver { + + private var binding: T? = null + + init { + controller.addLifecycleListener(object : Controller.LifecycleListener() { + override fun postDestroyView(controller: Controller) { + binding = null + } + }) + } + + override fun getValue(thisRef: Controller, property: KProperty<*>): T { + return binding ?: viewBinder(thisRef.view!!).also { binding = it } + } +} diff --git a/app/src/main/java/com/nextcloud/talk/models/database/User.java b/app/src/main/java/com/nextcloud/talk/models/database/CapabilitiesUtil.java similarity index 65% rename from app/src/main/java/com/nextcloud/talk/models/database/User.java rename to app/src/main/java/com/nextcloud/talk/models/database/CapabilitiesUtil.java index 7e599f782..35333920b 100644 --- a/app/src/main/java/com/nextcloud/talk/models/database/User.java +++ b/app/src/main/java/com/nextcloud/talk/models/database/CapabilitiesUtil.java @@ -1,7 +1,9 @@ /* * Nextcloud Talk application * + * @author Andy Scherzinger * @author Mario Danic + * Copyright (C) 2021 Andy Scherzinger (info@andy-scherzinger.de) * Copyright (C) 2017-2018 Mario Danic * * This program is free software: you can redistribute it and/or modify @@ -19,55 +21,24 @@ */ package com.nextcloud.talk.models.database; -import android.os.Parcelable; import android.util.Log; import com.bluelinelabs.logansquare.LoganSquare; import com.nextcloud.talk.models.json.capabilities.Capabilities; import java.io.IOException; -import java.io.Serializable; import java.util.HashMap; +import java.util.Map; -import io.requery.Entity; -import io.requery.Generated; -import io.requery.Key; -import io.requery.Persistable; +import androidx.annotation.Nullable; -@Entity -public interface User extends Parcelable, Persistable, Serializable { - static final String TAG = "UserEntity"; +public abstract class CapabilitiesUtil { + private static final String TAG = CapabilitiesUtil.class.getSimpleName(); - @Key - @Generated - long getId(); - - String getUserId(); - - String getUsername(); - - String getBaseUrl(); - - String getToken(); - - String getDisplayName(); - - String getPushConfigurationState(); - - String getCapabilities(); - - String getClientCertificate(); - - String getExternalSignalingServer(); - - boolean getCurrent(); - - boolean getScheduledForDeletion(); - - default boolean hasNotificationsCapability(String capabilityName) { - if (getCapabilities() != null) { + public static boolean hasNotificationsCapability(@Nullable UserEntity user, String capabilityName) { + if (user != null && user.getCapabilities() != null) { try { - Capabilities capabilities = LoganSquare.parse(getCapabilities(), Capabilities.class); + Capabilities capabilities = LoganSquare.parse(user.getCapabilities(), Capabilities.class); if (capabilities.getNotificationsCapability() != null && capabilities.getNotificationsCapability().getFeatures() != null) { return capabilities.getSpreedCapability().getFeatures().contains(capabilityName); } @@ -78,10 +49,10 @@ public interface User extends Parcelable, Persistable, Serializable { return false; } - default boolean hasExternalCapability(String capabilityName) { - if (getCapabilities() != null) { + public static boolean hasExternalCapability(@Nullable UserEntity user, String capabilityName) { + if (user != null && user.getCapabilities() != null) { try { - Capabilities capabilities = LoganSquare.parse(getCapabilities(), Capabilities.class); + Capabilities capabilities = LoganSquare.parse(user.getCapabilities(), Capabilities.class); if (capabilities.getExternalCapability() != null && capabilities.getExternalCapability().containsKey("v1")) { return capabilities.getExternalCapability().get("v1").contains("capabilityName"); } @@ -92,20 +63,20 @@ public interface User extends Parcelable, Persistable, Serializable { return false; } - default boolean isServerEOL() { + public static boolean isServerEOL(@Nullable UserEntity user) { // Capability is available since Talk 4 => Nextcloud 14 => Autmn 2018 - return !hasSpreedFeatureCapability("no-ping"); + return !hasSpreedFeatureCapability(user, "no-ping"); } - default boolean isServerAlmostEOL() { + public static boolean isServerAlmostEOL(@Nullable UserEntity user) { // Capability is available since Talk 8 => Nextcloud 18 => January 2020 - return !hasSpreedFeatureCapability("chat-replies"); + return !hasSpreedFeatureCapability(user, "chat-replies"); } - default boolean hasSpreedFeatureCapability(String capabilityName) { - if (getCapabilities() != null) { + public static boolean hasSpreedFeatureCapability(@Nullable UserEntity user, String capabilityName) { + if (user != null && user.getCapabilities() != null) { try { - Capabilities capabilities = LoganSquare.parse(getCapabilities(), Capabilities.class); + Capabilities capabilities = LoganSquare.parse(user.getCapabilities(), Capabilities.class); if (capabilities != null && capabilities.getSpreedCapability() != null && capabilities.getSpreedCapability().getFeatures() != null) { return capabilities.getSpreedCapability().getFeatures().contains(capabilityName); @@ -117,11 +88,10 @@ public interface User extends Parcelable, Persistable, Serializable { return false; } - default int getMessageMaxLength() { - if (getCapabilities() != null) { - Capabilities capabilities = null; + public static Integer getMessageMaxLength(@Nullable UserEntity user) { + if (user != null && user.getCapabilities() != null) { try { - capabilities = LoganSquare.parse(getCapabilities(), Capabilities.class); + Capabilities capabilities = LoganSquare.parse(user.getCapabilities(), Capabilities.class); if (capabilities != null && capabilities.getSpreedCapability() != null && capabilities.getSpreedCapability().getConfig() != null && capabilities.getSpreedCapability().getConfig().containsKey("chat")) { HashMap chatConfigHashMap = capabilities.getSpreedCapability().getConfig().get("chat"); @@ -135,52 +105,49 @@ public interface User extends Parcelable, Persistable, Serializable { } } } catch (IOException e) { - e.printStackTrace(); + Log.e(TAG, "Failed to get capabilities for the user"); } } return 1000; } - default boolean isPhoneBookIntegrationAvailable() { - if (getCapabilities() != null) { - Capabilities capabilities; + public static boolean isPhoneBookIntegrationAvailable(@Nullable UserEntity user) { + if (user != null && user.getCapabilities() != null) { try { - capabilities = LoganSquare.parse(getCapabilities(), Capabilities.class); + Capabilities capabilities = LoganSquare.parse(user.getCapabilities(), Capabilities.class); return capabilities != null && capabilities.getSpreedCapability() != null && capabilities.getSpreedCapability().getFeatures() != null && capabilities.getSpreedCapability().getFeatures().contains("phonebook-search"); } catch (IOException e) { - e.printStackTrace(); + Log.e(TAG, "Failed to get capabilities for the user"); } } return false; } - default boolean isReadStatusAvailable() { - if (getCapabilities() != null) { - Capabilities capabilities; + public static boolean isReadStatusAvailable(@Nullable UserEntity user) { + if (user != null && user.getCapabilities() != null) { try { - capabilities = LoganSquare.parse(getCapabilities(), Capabilities.class); + Capabilities capabilities = LoganSquare.parse(user.getCapabilities(), Capabilities.class); if (capabilities != null && capabilities.getSpreedCapability() != null && capabilities.getSpreedCapability().getConfig() != null && capabilities.getSpreedCapability().getConfig().containsKey("chat")) { - HashMap map = capabilities.getSpreedCapability().getConfig().get("chat"); + Map map = capabilities.getSpreedCapability().getConfig().get("chat"); return map != null && map.containsKey("read-privacy"); } } catch (IOException e) { - e.printStackTrace(); + Log.e(TAG, "Failed to get capabilities for the user"); } } return false; } - default boolean isReadStatusPrivate() { - if (getCapabilities() != null) { - Capabilities capabilities; + public static boolean isReadStatusPrivate(@Nullable UserEntity user) { + if (user != null && user.getCapabilities() != null) { try { - capabilities = LoganSquare.parse(getCapabilities(), Capabilities.class); + Capabilities capabilities = LoganSquare.parse(user.getCapabilities(), Capabilities.class); if (capabilities != null && capabilities.getSpreedCapability() != null && capabilities.getSpreedCapability().getConfig() != null && @@ -191,17 +158,16 @@ public interface User extends Parcelable, Persistable, Serializable { } } } catch (IOException e) { - e.printStackTrace(); + Log.e(TAG, "Failed to get capabilities for the user"); } } return false; } - default String getAttachmentFolder() { - if (getCapabilities() != null) { - Capabilities capabilities; + public static String getAttachmentFolder(@Nullable UserEntity user) { + if (user != null && user.getCapabilities() != null) { try { - capabilities = LoganSquare.parse(getCapabilities(), Capabilities.class); + Capabilities capabilities = LoganSquare.parse(user.getCapabilities(), Capabilities.class); if (capabilities != null && capabilities.getSpreedCapability() != null && capabilities.getSpreedCapability().getConfig() != null && @@ -218,11 +184,11 @@ public interface User extends Parcelable, Persistable, Serializable { return "/Talk"; } - default String getServerName() { - if (getCapabilities() != null) { + public static String getServerName(@Nullable UserEntity user) { + if (user != null && user.getCapabilities() != null) { Capabilities capabilities; try { - capabilities = LoganSquare.parse(getCapabilities(), Capabilities.class); + capabilities = LoganSquare.parse(user.getCapabilities(), Capabilities.class); if (capabilities != null && capabilities.getThemingCapability() != null) { return capabilities.getThemingCapability().getName(); } @@ -234,33 +200,33 @@ public interface User extends Parcelable, Persistable, Serializable { } // TODO later avatar can also be checked via user fields, for now it is in Talk capability - default boolean isAvatarEndpointAvailable() { - if (getCapabilities() != null) { + public static boolean isAvatarEndpointAvailable(@Nullable UserEntity user) { + if (user != null && user.getCapabilities() != null) { Capabilities capabilities; try { - capabilities = LoganSquare.parse(getCapabilities(), Capabilities.class); + capabilities = LoganSquare.parse(user.getCapabilities(), Capabilities.class); return (capabilities != null && capabilities.getSpreedCapability() != null && capabilities.getSpreedCapability().getFeatures() != null && capabilities.getSpreedCapability().getFeatures().contains("temp-user-avatar-api")); } catch (IOException e) { - e.printStackTrace(); + Log.e("User.java", "Failed to get server name", e); } } return false; } - default boolean canEditScopes() { - if (getCapabilities() != null) { + public static boolean canEditScopes(@Nullable UserEntity user) { + if (user != null && user.getCapabilities() != null) { Capabilities capabilities; try { - capabilities = LoganSquare.parse(getCapabilities(), Capabilities.class); + capabilities = LoganSquare.parse(user.getCapabilities(), Capabilities.class); return (capabilities != null && capabilities.getProvisioningCapability() != null && capabilities.getProvisioningCapability().getAccountPropertyScopesVersion() != null && capabilities.getProvisioningCapability().getAccountPropertyScopesVersion() > 1); } catch (IOException e) { - e.printStackTrace(); + Log.e("User.java", "Failed to get server name", e); } } return false; diff --git a/app/src/main/java/com/nextcloud/talk/models/database/User.kt b/app/src/main/java/com/nextcloud/talk/models/database/User.kt new file mode 100644 index 000000000..45a04e2ce --- /dev/null +++ b/app/src/main/java/com/nextcloud/talk/models/database/User.kt @@ -0,0 +1,53 @@ +/* + * Nextcloud Talk application + * + * @author Mario Danic + * Copyright (C) 2017-2018 Mario Danic + * + * 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 . + */ +package com.nextcloud.talk.models.database + +import android.os.Parcelable +import android.util.Log +import com.bluelinelabs.logansquare.LoganSquare +import com.nextcloud.talk.models.json.capabilities.Capabilities +import io.requery.Entity +import io.requery.Generated +import io.requery.Key +import io.requery.Persistable +import java.io.IOException +import java.io.Serializable + +@Entity +interface User : Parcelable, Persistable, Serializable { + @get:Generated + @get:Key + val id: Long + val userId: String? + val username: String? + val baseUrl: String? + val token: String? + val displayName: String? + val pushConfigurationState: String? + var capabilities: String? + val clientCertificate: String? + val externalSignalingServer: String? + val current: Boolean + val scheduledForDeletion: Boolean + + companion object { + const val TAG = "UserEntity" + } +} \ No newline at end of file diff --git a/app/src/main/java/com/nextcloud/talk/models/json/conversations/Conversation.java b/app/src/main/java/com/nextcloud/talk/models/json/conversations/Conversation.java index d068c2750..bf4f63c2f 100644 --- a/app/src/main/java/com/nextcloud/talk/models/json/conversations/Conversation.java +++ b/app/src/main/java/com/nextcloud/talk/models/json/conversations/Conversation.java @@ -22,6 +22,7 @@ package com.nextcloud.talk.models.json.conversations; import com.bluelinelabs.logansquare.annotation.JsonField; import com.bluelinelabs.logansquare.annotation.JsonObject; +import com.nextcloud.talk.models.database.CapabilitiesUtil; import com.nextcloud.talk.models.database.UserEntity; import com.nextcloud.talk.models.json.chat.ChatMessage; import com.nextcloud.talk.models.json.converters.EnumLobbyStateConverter; @@ -108,7 +109,8 @@ public class Conversation { } private boolean isLockedOneToOne(UserEntity conversationUser) { - return (getType() == ConversationType.ROOM_TYPE_ONE_TO_ONE_CALL && conversationUser.hasSpreedFeatureCapability("locked-one-to-one-rooms")); + return (getType() == ConversationType.ROOM_TYPE_ONE_TO_ONE_CALL && + CapabilitiesUtil.hasSpreedFeatureCapability(conversationUser, "locked-one-to-one-rooms")); } public boolean canModerate(UserEntity conversationUser) { diff --git a/app/src/main/java/com/nextcloud/talk/ui/dialog/AttachmentDialog.kt b/app/src/main/java/com/nextcloud/talk/ui/dialog/AttachmentDialog.kt index 921f2f2ab..29e47b131 100644 --- a/app/src/main/java/com/nextcloud/talk/ui/dialog/AttachmentDialog.kt +++ b/app/src/main/java/com/nextcloud/talk/ui/dialog/AttachmentDialog.kt @@ -31,6 +31,7 @@ import com.google.android.material.bottomsheet.BottomSheetDialog import com.nextcloud.talk.R import com.nextcloud.talk.components.filebrowser.controllers.BrowserController import com.nextcloud.talk.controllers.ChatController +import com.nextcloud.talk.models.database.CapabilitiesUtil class AttachmentDialog(val activity: Activity, var chatController: ChatController) : BottomSheetDialog(activity) { @@ -51,7 +52,7 @@ class AttachmentDialog(val activity: Activity, var chatController: ChatControlle window?.setLayout(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT) unbinder = ButterKnife.bind(this, view) - var serverName = chatController.conversationUser?.serverName + var serverName = CapabilitiesUtil.getServerName(chatController.conversationUser) attachFromCloud?.text = chatController.resources?.let { if (serverName.isNullOrEmpty()) { serverName = it.getString(R.string.nc_server_product_name) diff --git a/app/src/main/java/com/nextcloud/talk/utils/ApiUtils.java b/app/src/main/java/com/nextcloud/talk/utils/ApiUtils.java index a95717788..227d7a408 100644 --- a/app/src/main/java/com/nextcloud/talk/utils/ApiUtils.java +++ b/app/src/main/java/com/nextcloud/talk/utils/ApiUtils.java @@ -27,6 +27,7 @@ import com.nextcloud.talk.BuildConfig; import com.nextcloud.talk.R; import com.nextcloud.talk.application.NextcloudTalkApplication; import com.nextcloud.talk.models.RetrofitBucket; +import com.nextcloud.talk.models.database.CapabilitiesUtil; import com.nextcloud.talk.models.database.UserEntity; import java.util.HashMap; @@ -115,7 +116,7 @@ public class ApiUtils { return getConversationApiVersion(capabilities, versions); } - public static int getConversationApiVersion(UserEntity capabilities, int[] versions) throws NoSupportedApiException { + public static int getConversationApiVersion(UserEntity user, int[] versions) throws NoSupportedApiException { boolean hasApiV4 = false; for (int version : versions) { hasApiV4 |= version == 4; @@ -127,16 +128,17 @@ public class ApiUtils { } for (int version : versions) { - if (capabilities.hasSpreedFeatureCapability("conversation-v" + version)) { + if (CapabilitiesUtil.hasSpreedFeatureCapability(user, "conversation-v" + version)) { return version; } // Fallback for old API versions if ((version == 1 || version == 2)) { - if (capabilities.hasSpreedFeatureCapability("conversation-v2")) { + if (CapabilitiesUtil.hasSpreedFeatureCapability(user, "conversation-v2")) { return version; } - if (version == 1 && capabilities.hasSpreedFeatureCapability("conversation")) { + if (version == 1 && + CapabilitiesUtil.hasSpreedFeatureCapability(user, "conversation")) { return version; } } @@ -144,20 +146,20 @@ public class ApiUtils { throw new NoSupportedApiException(); } - public static int getSignalingApiVersion(UserEntity capabilities, int[] versions) throws NoSupportedApiException { + public static int getSignalingApiVersion(UserEntity user, int[] versions) throws NoSupportedApiException { for (int version : versions) { - if (capabilities.hasSpreedFeatureCapability("signaling-v" + version)) { + CapabilitiesUtil.hasSpreedFeatureCapability(user, "signaling-v" + version)) { return version; } if (version == 2 && - capabilities.hasSpreedFeatureCapability("sip-support") && - !capabilities.hasSpreedFeatureCapability("signaling-v3")) { + CapabilitiesUtil.hasSpreedFeatureCapability(user, "sip-support") && + !CapabilitiesUtil.hasSpreedFeatureCapability(user, "signaling-v3")) { return version; } if (version == 1 && - !capabilities.hasSpreedFeatureCapability("signaling-v3")) { + !CapabilitiesUtil.hasSpreedFeatureCapability(user, "signaling-v3")) { // Has no capability, we just assume it is always there when there is no v3 or later return version; } @@ -165,9 +167,9 @@ public class ApiUtils { throw new NoSupportedApiException(); } - public static int getChatApiVersion(UserEntity capabilities, int[] versions) throws NoSupportedApiException { + public static int getChatApiVersion(UserEntity user, int[] versions) throws NoSupportedApiException { for (int version : versions) { - if (version == 1 && capabilities.hasSpreedFeatureCapability("chat-v2")) { + if (version == 1 && CapabilitiesUtil.hasSpreedFeatureCapability(user, "chat-v2")) { // Do not question that chat-v2 capability shows the availability of api/v1/ endpoint *see no evil* return version; } diff --git a/app/src/main/java/com/nextcloud/talk/utils/preferences/preferencestorage/DatabaseStorageModule.java b/app/src/main/java/com/nextcloud/talk/utils/preferences/preferencestorage/DatabaseStorageModule.java index 9e6c29721..6712db869 100644 --- a/app/src/main/java/com/nextcloud/talk/utils/preferences/preferencestorage/DatabaseStorageModule.java +++ b/app/src/main/java/com/nextcloud/talk/utils/preferences/preferencestorage/DatabaseStorageModule.java @@ -26,6 +26,7 @@ import autodagger.AutoInjector; import com.nextcloud.talk.api.NcApi; import com.nextcloud.talk.application.NextcloudTalkApplication; import com.nextcloud.talk.models.database.ArbitraryStorageEntity; +import com.nextcloud.talk.models.database.CapabilitiesUtil; import com.nextcloud.talk.models.database.UserEntity; import com.nextcloud.talk.models.json.generic.GenericOverall; import com.nextcloud.talk.utils.ApiUtils; @@ -76,7 +77,7 @@ public class DatabaseStorageModule implements StorageModule { if (!key.equals("message_notification_level")) { arbitraryStorageUtils.storeStorageSetting(accountIdentifier, key, value, conversationToken); } else { - if (conversationUser.hasSpreedFeatureCapability("notification-levels")) { + if (CapabilitiesUtil.hasSpreedFeatureCapability(conversationUser, "notification-levels")) { if (!TextUtils.isEmpty(messageNotificationLevel) && !messageNotificationLevel.equals(value)) { int intValue; switch (value) { diff --git a/app/src/main/res/layout/controller_conversation_info.xml b/app/src/main/res/layout/controller_conversation_info.xml index ddd597588..09a791561 100644 --- a/app/src/main/res/layout/controller_conversation_info.xml +++ b/app/src/main/res/layout/controller_conversation_info.xml @@ -128,7 +128,7 @@ android:id="@+id/participants_list_category" android:layout_width="match_parent" android:layout_height="wrap_content" - android:layout_below="@+id/webinar_settings" + android:layout_below="@+id/settings" android:visibility="gone" apc:cardBackgroundColor="@color/bg_default" apc:cardElevation="0dp" @@ -180,21 +180,30 @@ - + android:orientation="vertical"> - + + + + +