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 0150bd8b4..71f099c26 100644 --- a/app/src/main/java/com/nextcloud/talk/controllers/ChatController.kt +++ b/app/src/main/java/com/nextcloud/talk/controllers/ChatController.kt @@ -292,7 +292,7 @@ class ChatController(args: Bundle) : lobbyTimerHandler = Handler() } - lobbyTimerHandler?.postDelayed({ getRoomInfo() }, 5000) + lobbyTimerHandler?.postDelayed({ getRoomInfo() }, LOBBY_TIMER_DELAY) } } }) @@ -460,7 +460,12 @@ class ChatController(args: Bundle) : } else { scrollPosition = newMessagesCount - 1 } - Handler().postDelayed({ binding.messagesListView.smoothScrollToPosition(scrollPosition) }, 200) + Handler().postDelayed( + { + binding.messagesListView.smoothScrollToPosition(scrollPosition) + }, + NEW_MESSAGES_POPUP_BUBBLE_DELAY + ) } } @@ -492,7 +497,7 @@ class ChatController(args: Bundle) : }) val filters = arrayOfNulls(1) - val lengthFilter = CapabilitiesUtil.getMessageMaxLength(conversationUser) ?: 1000 + val lengthFilter = CapabilitiesUtil.getMessageMaxLength(conversationUser) ?: MESSAGE_MAX_LENGTH filters[0] = InputFilter.LengthFilter(lengthFilter) binding.messageInputView.inputEditText?.filters = filters @@ -1330,7 +1335,7 @@ class ChatController(args: Bundle) : } } - if (response.code() == 200) { + if (response.code() == HTTP_CODE_OK) { val chatOverall = response.body() as ChatOverall? val chatMessageList = setDeletionFlagsAndRemoveInfomessages(chatOverall?.ocs!!.data) @@ -1836,10 +1841,9 @@ class ChatController(args: Bundle) : if (message.hasFileAttachment()) return false - val sixHoursInMillis = 6 * 3600 * 1000 val isOlderThanSixHours = message .createdAt - ?.before(Date(System.currentTimeMillis() - sixHoursInMillis)) == true + ?.before(Date(System.currentTimeMillis() - AGE_THREHOLD_FOR_DELETE_MESSAGE)) == true if (isOlderThanSixHours) return false val isUserAllowedByPrivileges = if (message.actorId == conversationUser.userId) { @@ -1855,12 +1859,11 @@ class ChatController(args: Bundle) : } override fun hasContentFor(message: IMessage, type: Byte): Boolean { - when (type) { - CONTENT_TYPE_SYSTEM_MESSAGE -> return !TextUtils.isEmpty(message.systemMessage) - CONTENT_TYPE_UNREAD_NOTICE_MESSAGE -> return message.id == "-1" + return when (type) { + CONTENT_TYPE_SYSTEM_MESSAGE -> !TextUtils.isEmpty(message.systemMessage) + CONTENT_TYPE_UNREAD_NOTICE_MESSAGE -> message.id == "-1" + else -> false } - - return false } @Subscribe(threadMode = ThreadMode.BACKGROUND) @@ -1942,7 +1945,7 @@ class ChatController(args: Bundle) : router.popCurrentController() } }, - 100 + POP_CURRENT_CONTROLLER_DELAY ) } } @@ -1959,9 +1962,15 @@ class ChatController(args: Bundle) : } companion object { - private val TAG = "ChatController" - private val CONTENT_TYPE_SYSTEM_MESSAGE: Byte = 1 - private val CONTENT_TYPE_UNREAD_NOTICE_MESSAGE: Byte = 2 - val REQUEST_CODE_CHOOSE_FILE: Int = 555 + private const val TAG = "ChatController" + private const val CONTENT_TYPE_SYSTEM_MESSAGE: Byte = 1 + private const val CONTENT_TYPE_UNREAD_NOTICE_MESSAGE: Byte = 2 + private const val NEW_MESSAGES_POPUP_BUBBLE_DELAY: Long = 200 + private const val POP_CURRENT_CONTROLLER_DELAY: Long = 100 + private const val LOBBY_TIMER_DELAY: Long = 5000 + private const val HTTP_CODE_OK: Int = 200 + private const val MESSAGE_MAX_LENGTH: Int = 1000 + private const val AGE_THREHOLD_FOR_DELETE_MESSAGE: Int = 21600000 // (6 hours in millis = 6 * 3600 * 1000) + private const val REQUEST_CODE_CHOOSE_FILE: Int = 555 } } 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 8d8db1b9a..f3069485b 100644 --- a/app/src/main/java/com/nextcloud/talk/controllers/ConversationInfoController.kt +++ b/app/src/main/java/com/nextcloud/talk/controllers/ConversationInfoController.kt @@ -181,10 +181,7 @@ class ConversationInfoController(args: Bundle) : private fun setupWebinaryView() { if (CapabilitiesUtil.hasSpreedFeatureCapability(conversationUser, "webinary-lobby") && - ( - conversation!!.type == Conversation.ConversationType.ROOM_GROUP_CALL || - conversation!!.type == Conversation.ConversationType.ROOM_PUBLIC_CALL - ) && + webinaryRoomType(conversation!!) && conversation!!.canModerate(conversationUser) ) { binding.webinarInfoView.webinarSettings.visibility = View.VISIBLE @@ -227,6 +224,11 @@ class ConversationInfoController(args: Bundle) : } } + private fun webinaryRoomType(conversation: Conversation): Boolean { + return conversation.type == Conversation.ConversationType.ROOM_GROUP_CALL || + conversation.type == Conversation.ConversationType.ROOM_PUBLIC_CALL + } + fun reconfigureLobbyTimerView(dateTime: Calendar? = null) { val isChecked = (binding.webinarInfoView.conversationInfoLobby.findViewById(R.id.mp_checkable) as SwitchCompat) @@ -595,7 +597,7 @@ class ConversationInfoController(args: Bundle) : } } else { binding.notificationSettingsView.conversationInfoMessageNotifications.isEnabled = false - binding.notificationSettingsView.conversationInfoMessageNotifications.alpha = 0.38f + binding.notificationSettingsView.conversationInfoMessageNotifications.alpha = LOW_EMPHASIS_OPACITY setProperNotificationValue(conversation) } } @@ -963,9 +965,9 @@ class ConversationInfoController(args: Bundle) : } companion object { - private const val TAG = "ConversationInfoController" private const val ID_DELETE_CONVERSATION_DIALOG = 0 + private val LOW_EMPHASIS_OPACITY: Float = 0.38f } /** 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 index 101524787..7771cb4cb 100644 --- a/app/src/main/java/com/nextcloud/talk/controllers/base/NewBaseController.kt +++ b/app/src/main/java/com/nextcloud/talk/controllers/base/NewBaseController.kt @@ -78,6 +78,7 @@ abstract class NewBaseController(@LayoutRes var layoutRes: Int, args: Bundle? = protected open val title: String? get() = null + @Suppress("Detekt.TooGenericExceptionCaught") protected val actionBar: ActionBar? get() { var actionBarProvider: ActionBarProvider? = null @@ -224,20 +225,32 @@ abstract class NewBaseController(@LayoutRes var layoutRes: Int, args: Bundle? = } protected fun setTitle() { - if (title != null && actionBar != null) { + if (isTitleSetable()) { run { - var parentController = parentController - while (parentController != null) { - if (parentController is BaseController && parentController.title != null) { - return - } - parentController = parentController.parentController - } + calculateValidParentController() } actionBar!!.title = title } } + private fun calculateValidParentController() { + var parentController = parentController + while (parentController != null) { + if (isValidController(parentController)) { + return + } + parentController = parentController.parentController + } + } + + private fun isValidController(parentController: Controller): Boolean { + return parentController is BaseController && parentController.title != null + } + + private fun isTitleSetable(): Boolean { + return title != null && actionBar != null + } + override fun onOptionsItemSelected(item: MenuItem): Boolean { if (item.itemId == android.R.id.home) { router.popCurrentController() 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 5982902cd..d1226ba3a 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 @@ -157,6 +157,7 @@ public class OperationsMenuController extends BaseController { } + @NonNull @Override protected View inflateView(@NonNull LayoutInflater inflater, @NonNull ViewGroup container) { return inflater.inflate(R.layout.controller_operations_menu, container, false); @@ -222,17 +223,23 @@ public class OperationsMenuController extends BaseController { .observeOn(AndroidSchedulers.mainThread()) .subscribe(new Observer() { @Override - public void onSubscribe(Disposable d) { + public void onSubscribe(@io.reactivex.annotations.NonNull Disposable d) { } @SuppressLint("LongLogTag") @Override - public void onNext(CapabilitiesOverall capabilitiesOverall) { + public void onNext(@io.reactivex.annotations.NonNull CapabilitiesOverall capabilitiesOverall) { currentUser = new UserEntity(); currentUser.setBaseUrl(baseUrl); currentUser.setUserId("?"); try { - currentUser.setCapabilities(LoganSquare.serialize(capabilitiesOverall.getOcs().getData().getCapabilities())); + currentUser.setCapabilities( + LoganSquare + .serialize( + capabilitiesOverall + .getOcs() + .getData() + .getCapabilities())); } catch (IOException e) { Log.e("OperationsMenu", "Failed to serialize capabilities"); } @@ -248,7 +255,7 @@ public class OperationsMenuController extends BaseController { @SuppressLint("LongLogTag") @Override - public void onError(Throwable e) { + public void onError(@io.reactivex.annotations.NonNull Throwable e) { showResultImage(false, false); Log.e(TAG, "Error fetching capabilities for guest", e); } @@ -325,12 +332,12 @@ public class OperationsMenuController extends BaseController { .retry(1) .subscribe(new Observer() { @Override - public void onSubscribe(Disposable d) { + public void onSubscribe(@io.reactivex.annotations.NonNull Disposable d) { disposable = d; } @Override - public void onNext(RoomOverall roomOverall) { + public void onNext(@io.reactivex.annotations.NonNull RoomOverall roomOverall) { conversation = roomOverall.getOcs().getData(); if (conversation.isHasPassword() && conversation.isGuest()) { eventBus.post(new BottomSheetLockEvent(true, 0, @@ -358,25 +365,27 @@ public class OperationsMenuController extends BaseController { .observeOn(AndroidSchedulers.mainThread()) .subscribe(new Observer() { @Override - public void onSubscribe(Disposable d) { - + public void onSubscribe( + @io.reactivex.annotations.NonNull Disposable d + ) { } @Override - public void onNext(RoomOverall roomOverall) { + public void onNext( + @io.reactivex.annotations.NonNull RoomOverall roomOverall + ) { conversation = roomOverall.getOcs().getData(); initiateConversation(false); } @Override - public void onError(Throwable e) { + public void onError(@io.reactivex.annotations.NonNull Throwable e) { showResultImage(false, false); dispose(); } @Override public void onComplete() { - } }); } else { @@ -385,7 +394,7 @@ public class OperationsMenuController extends BaseController { } @Override - public void onError(Throwable e) { + public void onError(@io.reactivex.annotations.NonNull Throwable e) { showResultImage(false, false); dispose(); } @@ -418,12 +427,12 @@ public class OperationsMenuController extends BaseController { .retry(1) .subscribe(new Observer() { @Override - public void onSubscribe(Disposable d) { + public void onSubscribe(@io.reactivex.annotations.NonNull Disposable d) { } @Override - public void onNext(RoomOverall roomOverall) { + public void onNext(@io.reactivex.annotations.NonNull RoomOverall roomOverall) { conversation = roomOverall.getOcs().getData(); ncApi.getRoom(credentials, @@ -433,18 +442,20 @@ public class OperationsMenuController extends BaseController { .observeOn(AndroidSchedulers.mainThread()) .subscribe(new Observer() { @Override - public void onSubscribe(Disposable d) { + public void onSubscribe(@io.reactivex.annotations.NonNull Disposable d) { } @Override - public void onNext(RoomOverall roomOverall) { + public void onNext( + @io.reactivex.annotations.NonNull RoomOverall roomOverall + ) { conversation = roomOverall.getOcs().getData(); inviteUsersToAConversation(); } @Override - public void onError(Throwable e) { + public void onError(@io.reactivex.annotations.NonNull Throwable e) { showResultImage(false, false); dispose(); } @@ -458,7 +469,7 @@ public class OperationsMenuController extends BaseController { } @Override - public void onError(Throwable e) { + public void onError(@io.reactivex.annotations.NonNull Throwable e) { showResultImage(false, false); dispose(); } @@ -510,12 +521,16 @@ public class OperationsMenuController extends BaseController { private void showResultImage(boolean everythingOK, boolean isGuestSupportError) { progressBar.setVisibility(View.GONE); - if (everythingOK) { - resultImageView.setImageDrawable(DisplayUtils.getTintedDrawable(getResources(), R.drawable - .ic_check_circle_black_24dp, R.color.nc_darkGreen)); - } else { - resultImageView.setImageDrawable(DisplayUtils.getTintedDrawable(getResources(), R.drawable - .ic_cancel_black_24dp, R.color.nc_darkRed)); + if (getResources() != null) { + if (everythingOK) { + resultImageView.setImageDrawable(DisplayUtils.getTintedDrawable(getResources(), + R.drawable.ic_check_circle_black_24dp, + R.color.nc_darkGreen)); + } else { + resultImageView.setImageDrawable(DisplayUtils.getTintedDrawable(getResources(), + R.drawable.ic_cancel_black_24dp, + R.color.nc_darkRed)); + } } resultImageView.setVisibility(View.VISIBLE); @@ -583,59 +598,79 @@ public class OperationsMenuController extends BaseController { 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( - apiVersion, - currentUser.getBaseUrl(), - conversation.getToken(), - "groups", - groupId - ); + addGroupsToConversation(localInvitedUsers, localInvitedGroups, apiVersion); + addUsersToConversation(localInvitedUsers, localInvitedGroups, apiVersion); + } else { + initiateConversation(true); + } + } - ncApi.addParticipant(credentials, retrofitBucket.getUrl(), retrofitBucket.getQueryMap()) - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .retry(1) - .subscribe(new Observer() { - @Override - public void onSubscribe(Disposable d) { + private void addUsersToConversation( + ArrayList localInvitedUsers, + ArrayList localInvitedGroups, + int apiVersion) + { + RetrofitBucket retrofitBucket; + for (int i = 0; i < localInvitedUsers.size(); i++) { + final String userId = invitedUsers.get(i); + retrofitBucket = ApiUtils.getRetrofitBucketForAddParticipant(apiVersion, + currentUser.getBaseUrl(), + conversation.getToken(), + userId); - } + ncApi.addParticipant(credentials, retrofitBucket.getUrl(), retrofitBucket.getQueryMap()) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .retry(1) + .subscribe(new Observer() { + @Override + public void onSubscribe(@io.reactivex.annotations.NonNull Disposable d) { - @Override - public void onNext(AddParticipantOverall addParticipantOverall) { - } + } - @Override - public void onError(Throwable e) { - dispose(); - } + @Override + public void onNext( + @io.reactivex.annotations.NonNull AddParticipantOverall addParticipantOverall + ) { + } - @Override - public void onComplete() { - synchronized (localInvitedGroups) { - localInvitedGroups.remove(groupId); - } + @Override + public void onError(@io.reactivex.annotations.NonNull Throwable e) { + dispose(); + } - if (localInvitedGroups.size() == 0 && localInvitedUsers.size() == 0) { - initiateConversation(true); - } - dispose(); - } - }); + @Override + public void onComplete() { + synchronized (localInvitedUsers) { + localInvitedUsers.remove(userId); + } - } - } + if (localInvitedGroups.size() == 0 && localInvitedUsers.size() == 0) { + initiateConversation(true); + } + dispose(); + } + }); + } + } - for (int i = 0; i < localInvitedUsers.size(); i++) { - final String userId = invitedUsers.get(i); - retrofitBucket = ApiUtils.getRetrofitBucketForAddParticipant(apiVersion, - currentUser.getBaseUrl(), - conversation.getToken(), - userId); + private void addGroupsToConversation( + ArrayList localInvitedUsers, + ArrayList localInvitedGroups, + int apiVersion) + { + RetrofitBucket retrofitBucket; + 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( + apiVersion, + currentUser.getBaseUrl(), + conversation.getToken(), + "groups", + groupId + ); ncApi.addParticipant(credentials, retrofitBucket.getUrl(), retrofitBucket.getQueryMap()) .subscribeOn(Schedulers.io()) @@ -643,23 +678,25 @@ public class OperationsMenuController extends BaseController { .retry(1) .subscribe(new Observer() { @Override - public void onSubscribe(Disposable d) { + public void onSubscribe(@io.reactivex.annotations.NonNull Disposable d) { } @Override - public void onNext(AddParticipantOverall addParticipantOverall) { + public void onNext( + @io.reactivex.annotations.NonNull AddParticipantOverall addParticipantOverall + ) { } @Override - public void onError(Throwable e) { + public void onError(@io.reactivex.annotations.NonNull Throwable e) { dispose(); } @Override public void onComplete() { - synchronized (localInvitedUsers) { - localInvitedUsers.remove(userId); + synchronized (localInvitedGroups) { + localInvitedGroups.remove(groupId); } if (localInvitedGroups.size() == 0 && localInvitedUsers.size() == 0) { @@ -668,9 +705,8 @@ public class OperationsMenuController extends BaseController { dispose(); } }); + } - } else { - initiateConversation(true); } } diff --git a/app/src/main/java/com/nextcloud/talk/jobs/UploadAndShareFilesWorker.kt b/app/src/main/java/com/nextcloud/talk/jobs/UploadAndShareFilesWorker.kt index f2d91f747..e9099c2a3 100644 --- a/app/src/main/java/com/nextcloud/talk/jobs/UploadAndShareFilesWorker.kt +++ b/app/src/main/java/com/nextcloud/talk/jobs/UploadAndShareFilesWorker.kt @@ -193,16 +193,16 @@ class UploadAndShareFilesWorker(val context: Context, workerParameters: WorkerPa fun isStoragePermissionGranted(context: Context): Boolean { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - if (PermissionChecker.checkSelfPermission( + return if (PermissionChecker.checkSelfPermission( context, Manifest.permission.WRITE_EXTERNAL_STORAGE ) == PermissionChecker.PERMISSION_GRANTED ) { Log.d(TAG, "Permission is granted") - return true + true } else { Log.d(TAG, "Permission is revoked") - return false + false } } else { // permission is automatically granted on sdk<23 upon installation Log.d(TAG, "Permission is granted") diff --git a/detekt.yml b/detekt.yml index 36a91e080..d93d0475f 100644 --- a/detekt.yml +++ b/detekt.yml @@ -1,5 +1,5 @@ build: - maxIssues: 217 + maxIssues: 201 weights: # complexity: 2 # LongParameterList: 1