Merge pull request #2326 from nextcloud/feature/2134/allow-guests

Move allow guests preferences to conversation info
This commit is contained in:
Tim Krüger 2022-08-29 20:19:37 +02:00 committed by GitHub
commit cd25a7e14d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
22 changed files with 768 additions and 437 deletions

View file

@ -1707,8 +1707,8 @@ public class CallActivity extends CallBaseActivity {
}
}
for (int i = 0; i < peerConnectionWrapperList.size(); i++) {
endPeerConnection(peerConnectionWrapperList.get(i).getSessionId(), false);
for (PeerConnectionWrapper wrapper : peerConnectionWrapperList) {
endPeerConnection(wrapper.getSessionId(), false);
}
if (localStream != null) {
@ -1887,10 +1887,10 @@ public class CallActivity extends CallBaseActivity {
}
private PeerConnectionWrapper getPeerConnectionWrapperForSessionIdAndType(String sessionId, String type) {
for (int i = 0; i < peerConnectionWrapperList.size(); i++) {
if (peerConnectionWrapperList.get(i).getSessionId().equals(sessionId)
&& peerConnectionWrapperList.get(i).getVideoStreamType().equals(type)) {
return peerConnectionWrapperList.get(i);
for (PeerConnectionWrapper wrapper : peerConnectionWrapperList) {
if (wrapper.getSessionId().equals(sessionId)
&& wrapper.getVideoStreamType().equals(type)) {
return wrapper;
}
}
@ -1980,10 +1980,8 @@ public class CallActivity extends CallBaseActivity {
private void endPeerConnection(String sessionId, boolean justScreen) {
List<PeerConnectionWrapper> peerConnectionWrappers;
PeerConnectionWrapper peerConnectionWrapper;
if (!(peerConnectionWrappers = getPeerConnectionWrapperListForSessionId(sessionId)).isEmpty()) {
for (int i = 0; i < peerConnectionWrappers.size(); i++) {
peerConnectionWrapper = peerConnectionWrappers.get(i);
for (PeerConnectionWrapper peerConnectionWrapper : peerConnectionWrappers) {
if (peerConnectionWrapper.getSessionId().equals(sessionId)) {
if (VIDEO_STREAM_TYPE_SCREEN.equals(peerConnectionWrapper.getVideoStreamType()) || !justScreen) {
runOnUiThread(() -> removeMediaStream(sessionId));
@ -2104,10 +2102,8 @@ public class CallActivity extends CallBaseActivity {
nickChangedPayload.put("userid", conversationUser.getUserId());
nickChangedPayload.put("name", conversationUser.getDisplayName());
dataChannelMessage.setPayload(nickChangedPayload);
final PeerConnectionWrapper peerConnectionWrapper;
for (int i = 0; i < peerConnectionWrapperList.size(); i++) {
if (peerConnectionWrapperList.get(i).isMCUPublisher()) {
peerConnectionWrapper = peerConnectionWrapperList.get(i);
for (PeerConnectionWrapper peerConnectionWrapper : peerConnectionWrapperList) {
if (peerConnectionWrapper.isMCUPublisher()) {
Observable
.interval(1, TimeUnit.SECONDS)
.repeatUntil(() -> (!isConnectionEstablished() || isDestroyed()))

View file

@ -142,6 +142,9 @@ public interface NcApi {
Observable<AddParticipantOverall> addParticipant(@Header("Authorization") String authorization, @Url String url,
@QueryMap Map<String, String> options);
@POST
Observable<GenericOverall> resendParticipantInvitations(@Header("Authorization") String authorization,
@Url String url);
// also used for removing a guest from a conversation
@Deprecated
@ -313,6 +316,12 @@ public interface NcApi {
Observable<GenericOverall> setPassword(@Header("Authorization") String authorization, @Url String url,
@Field("password") String password);
@FormUrlEncoded
@PUT
Observable<Response<GenericOverall>> setPassword2(@Header("Authorization") String authorization,
@Url String url,
@Field("password") String password);
@GET
Observable<CapabilitiesOverall> getCapabilities(@Header("Authorization") String authorization, @Url String url);

View file

@ -6,7 +6,7 @@
* @author Tim Krüger
* @author Marcel Hibbe
* Copyright (C) 2022 Marcel Hibbe (dev@mhibbe.de)
* Copyright (C) 2021 Tim Krüger <t@timkrueger.me>
* Copyright (C) 2021-2022 Tim Krüger <t@timkrueger.me>
* Copyright (C) 2021 Andy Scherzinger (info@andy-scherzinger.de)
* Copyright (C) 2017-2018 Mario Danic <mario@lovelyhq.com>
*
@ -37,6 +37,8 @@ import android.text.TextUtils
import android.util.Log
import android.view.MenuItem
import android.view.View
import android.view.View.GONE
import android.view.View.VISIBLE
import android.widget.Toast
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.widget.SwitchCompat
@ -61,6 +63,7 @@ import com.nextcloud.talk.controllers.base.BaseController
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.conversation.info.GuestAccessHelper
import com.nextcloud.talk.data.user.model.User
import com.nextcloud.talk.databinding.ControllerConversationInfoBinding
import com.nextcloud.talk.events.EventStatus
@ -75,6 +78,7 @@ import com.nextcloud.talk.models.json.participants.Participant.ActorType.CIRCLES
import com.nextcloud.talk.models.json.participants.Participant.ActorType.GROUPS
import com.nextcloud.talk.models.json.participants.Participant.ActorType.USERS
import com.nextcloud.talk.models.json.participants.ParticipantsOverall
import com.nextcloud.talk.repositories.conversations.ConversationsRepository
import com.nextcloud.talk.shareditems.activities.SharedItemsActivity
import com.nextcloud.talk.utils.ApiUtils
import com.nextcloud.talk.utils.DateConstants
@ -110,6 +114,9 @@ class ConversationInfoController(args: Bundle) :
@Inject
lateinit var ncApi: NcApi
@Inject
lateinit var conversationsRepository: ConversationsRepository
@Inject
lateinit var eventBus: EventBus
@ -167,6 +174,7 @@ class ConversationInfoController(args: Bundle) :
binding.notificationSettingsView.notificationSettings.setStorageModule(databaseStorageModule)
binding.webinarInfoView.webinarSettings.setStorageModule(databaseStorageModule)
binding.guestAccessView.guestAccessSettings.setStorageModule(databaseStorageModule)
binding.deleteConversationAction.setOnClickListener { showDeleteConversationDialog() }
binding.leaveConversationAction.setOnClickListener { leaveConversation() }
@ -176,7 +184,7 @@ class ConversationInfoController(args: Bundle) :
if (CapabilitiesUtilNew.hasSpreedFeatureCapability(conversationUser, "rich-object-list-media")) {
binding.showSharedItemsAction.setOnClickListener { showSharedItems() }
} else {
binding.categorySharedItems.visibility = View.GONE
binding.categorySharedItems.visibility = GONE
}
fetchRoomInfo()
@ -190,7 +198,9 @@ class ConversationInfoController(args: Bundle) :
listOf(
binding.webinarInfoView.conversationInfoLobby,
binding.notificationSettingsView.callNotifications,
binding.notificationSettingsView.conversationInfoPriorityConversation
binding.notificationSettingsView.conversationInfoPriorityConversation,
binding.guestAccessView.guestAccessAllowSwitch,
binding.guestAccessView.guestAccessPasswordSwitch
).forEach(viewThemeUtils::colorSwitchPreference)
}
}
@ -205,6 +215,7 @@ class ConversationInfoController(args: Bundle) :
ownOptions,
categorySharedItems,
categoryConversationSettings,
binding.guestAccessView.guestAccessCategory,
binding.webinarInfoView.conversationInfoWebinar,
binding.notificationSettingsView.notificationSettingsCategory
).forEach(viewThemeUtils::colorPreferenceCategory)
@ -225,7 +236,7 @@ class ConversationInfoController(args: Bundle) :
override fun onViewBound(view: View) {
super.onViewBound(view)
binding.addParticipantsAction.visibility = View.GONE
binding.addParticipantsAction.visibility = GONE
viewThemeUtils.colorCircularProgressBar(binding.progressBar)
}
@ -235,7 +246,7 @@ class ConversationInfoController(args: Bundle) :
webinaryRoomType(conversation!!) &&
conversation!!.canModerate(conversationUser!!)
) {
binding.webinarInfoView.webinarSettings.visibility = View.VISIBLE
binding.webinarInfoView.webinarSettings.visibility = VISIBLE
val isLobbyOpenToModeratorsOnly =
conversation!!.lobbyState == Conversation.LobbyState.LOBBY_STATE_MODERATORS_ONLY
@ -271,7 +282,7 @@ class ConversationInfoController(args: Bundle) :
submitLobbyChanges()
}
} else {
binding.webinarInfoView.webinarSettings.visibility = View.GONE
binding.webinarInfoView.webinarSettings.visibility = GONE
}
}
@ -311,9 +322,9 @@ class ConversationInfoController(args: Bundle) :
}
if (isChecked) {
binding.webinarInfoView.startTimePreferences.visibility = View.VISIBLE
binding.webinarInfoView.startTimePreferences.visibility = VISIBLE
} else {
binding.webinarInfoView.startTimePreferences.visibility = View.GONE
binding.webinarInfoView.startTimePreferences.visibility = GONE
}
}
@ -433,7 +444,7 @@ class ConversationInfoController(args: Bundle) :
setupAdapter()
binding.participantsListCategory.visibility = View.VISIBLE
binding.participantsListCategory.visibility = VISIBLE
adapter!!.updateDataSet(userItems)
}
@ -523,7 +534,7 @@ class ConversationInfoController(args: Bundle) :
private fun leaveConversation() {
workerData?.let {
WorkManager.getInstance().enqueue(
WorkManager.getInstance(context).enqueue(
OneTimeWorkRequest.Builder(
LeaveConversationWorker::class
.java
@ -586,7 +597,7 @@ class ConversationInfoController(args: Bundle) :
private fun deleteConversation() {
workerData?.let {
WorkManager.getInstance().enqueue(
WorkManager.getInstance(context).enqueue(
OneTimeWorkRequest.Builder(
DeleteConversationWorker::class.java
).setInputData(it).build()
@ -624,40 +635,40 @@ class ConversationInfoController(args: Bundle) :
val conversationCopy = conversation
if (conversationCopy!!.canModerate(conversationUser)) {
binding.addParticipantsAction.visibility = View.VISIBLE
binding.addParticipantsAction.visibility = VISIBLE
if (CapabilitiesUtilNew.hasSpreedFeatureCapability(conversationUser, "clear-history")) {
binding.clearConversationHistory.visibility = View.VISIBLE
binding.clearConversationHistory.visibility = VISIBLE
} else {
binding.clearConversationHistory.visibility = View.GONE
binding.clearConversationHistory.visibility = GONE
}
} else {
binding.addParticipantsAction.visibility = View.GONE
binding.clearConversationHistory.visibility = View.GONE
binding.addParticipantsAction.visibility = GONE
binding.clearConversationHistory.visibility = GONE
}
if (isAttached && (!isBeingDestroyed || !isDestroyed)) {
binding.ownOptions.visibility = View.VISIBLE
binding.ownOptions.visibility = VISIBLE
setupWebinaryView()
if (!conversation!!.canLeave()) {
binding.leaveConversationAction.visibility = View.GONE
binding.leaveConversationAction.visibility = GONE
} else {
binding.leaveConversationAction.visibility = View.VISIBLE
binding.leaveConversationAction.visibility = VISIBLE
}
if (!conversation!!.canDelete(conversationUser)) {
binding.deleteConversationAction.visibility = View.GONE
binding.deleteConversationAction.visibility = GONE
} else {
binding.deleteConversationAction.visibility = View.VISIBLE
binding.deleteConversationAction.visibility = VISIBLE
}
if (Conversation.ConversationType.ROOM_SYSTEM == conversation!!.type) {
binding.notificationSettingsView.callNotifications.visibility = View.GONE
binding.notificationSettingsView.callNotifications.visibility = GONE
}
if (conversation!!.notificationCalls === null) {
binding.notificationSettingsView.callNotifications.visibility = View.GONE
binding.notificationSettingsView.callNotifications.visibility = GONE
} else {
binding.notificationSettingsView.callNotifications.value =
conversationCopy.notificationCalls == 1
@ -665,22 +676,29 @@ class ConversationInfoController(args: Bundle) :
getListOfParticipants()
binding.progressBar.visibility = View.GONE
binding.progressBar.visibility = GONE
binding.conversationInfoName.visibility = View.VISIBLE
binding.conversationInfoName.visibility = VISIBLE
binding.displayNameText.text = conversation!!.displayName
if (conversation!!.description != null && !conversation!!.description!!.isEmpty()) {
binding.descriptionText.text = conversation!!.description
binding.conversationDescription.visibility = View.VISIBLE
binding.conversationDescription.visibility = VISIBLE
}
loadConversationAvatar()
adjustNotificationLevelUI()
initExpiringMessageOption()
binding.notificationSettingsView.notificationSettings.visibility = View.VISIBLE
GuestAccessHelper(
this@ConversationInfoController,
binding,
conversation!!,
conversationUser
).setupGuestAccess()
binding.notificationSettingsView.notificationSettings.visibility = VISIBLE
}
} catch (npe: NullPointerException) {
// view binding can be null
@ -1035,7 +1053,7 @@ class ConversationInfoController(args: Bundle) :
cornerRadius(res = R.dimen.corner_radius)
title(text = participant.displayName)
listItemsWithImage(items = items) { dialog, index, _ ->
listItemsWithImage(items = items) { _, index, _ ->
if (index == 0) {
removeAttendeeFromConversation(apiVersion, participant)
}
@ -1061,7 +1079,7 @@ class ConversationInfoController(args: Bundle) :
cornerRadius(res = R.dimen.corner_radius)
title(text = participant.displayName)
listItemsWithImage(items = items) { dialog, index, _ ->
listItemsWithImage(items = items) { _, index, _ ->
if (index == 0) {
removeAttendeeFromConversation(apiVersion, participant)
}
@ -1081,7 +1099,7 @@ class ConversationInfoController(args: Bundle) :
cornerRadius(res = R.dimen.corner_radius)
title(text = participant.displayName)
listItemsWithImage(items = items) { dialog, index, _ ->
listItemsWithImage(items = items) { _, index, _ ->
if (index == 0) {
removeAttendeeFromConversation(apiVersion, participant)
}
@ -1132,7 +1150,7 @@ class ConversationInfoController(args: Bundle) :
cornerRadius(res = R.dimen.corner_radius)
title(text = participant.displayName)
listItemsWithImage(items = items) { dialog, index, _ ->
listItemsWithImage(items = items) { _, index, _ ->
var actionToTrigger = index
if (participant.attendeePin == null || participant.attendeePin!!.isEmpty()) {
actionToTrigger++

View file

@ -22,12 +22,6 @@ package com.nextcloud.talk.controllers.bottomsheet
enum class ConversationOperationEnum {
OPS_CODE_RENAME_ROOM,
OPS_CODE_MAKE_PUBLIC,
OPS_CODE_CHANGE_PASSWORD,
OPS_CODE_CLEAR_PASSWORD,
OPS_CODE_SET_PASSWORD,
OPS_CODE_SHARE_LINK,
OPS_CODE_MAKE_PRIVATE,
OPS_CODE_GET_AND_JOIN_ROOM,
OPS_CODE_INVITE_USERS,
OPS_CODE_MARK_AS_READ,

View file

@ -23,7 +23,6 @@
*/
package com.nextcloud.talk.controllers.bottomsheet
import android.content.ComponentName
import android.content.Intent
import android.content.res.ColorStateList
import android.os.Bundle
@ -47,7 +46,6 @@ import com.nextcloud.talk.controllers.util.viewBinding
import com.nextcloud.talk.databinding.ControllerEntryMenuBinding
import com.nextcloud.talk.models.json.conversations.Conversation
import com.nextcloud.talk.users.UserManager
import com.nextcloud.talk.utils.ShareUtils
import com.nextcloud.talk.utils.UriUtils
import com.nextcloud.talk.utils.bundle.BundleKeys
import com.nextcloud.talk.utils.singletons.ApplicationWideMessageHolder
@ -139,13 +137,6 @@ class EntryMenuController(args: Bundle) :
)
}
ConversationOperationEnum.OPS_CODE_CHANGE_PASSWORD -> {
labelText = resources!!.getString(R.string.nc_new_password)
binding.textEdit.inputType = InputType.TYPE_CLASS_TEXT or InputType.TYPE_TEXT_VARIATION_PASSWORD
}
ConversationOperationEnum.OPS_CODE_SET_PASSWORD,
ConversationOperationEnum.OPS_CODE_SHARE_LINK,
ConversationOperationEnum.OPS_CODE_JOIN_ROOM -> {
// 99 is joining a conversation via password
labelText = resources!!.getString(R.string.nc_password)
@ -242,18 +233,12 @@ class EntryMenuController(args: Bundle) :
private fun onOkButtonClick() {
if (operation === ConversationOperationEnum.OPS_CODE_JOIN_ROOM) {
joinRoom()
} else if (operation !== ConversationOperationEnum.OPS_CODE_SHARE_LINK &&
} else if (
operation !== ConversationOperationEnum.OPS_CODE_GET_AND_JOIN_ROOM &&
operation !== ConversationOperationEnum.OPS_CODE_INVITE_USERS
) {
val bundle = Bundle()
if (operation === ConversationOperationEnum.OPS_CODE_CHANGE_PASSWORD ||
operation === ConversationOperationEnum.OPS_CODE_SET_PASSWORD
) {
conversation!!.password = binding.textEdit.text.toString()
} else {
conversation!!.name = binding.textEdit.text.toString()
}
conversation!!.name = binding.textEdit.text.toString()
bundle.putParcelable(BundleKeys.KEY_ROOM, Parcels.wrap<Any>(conversation))
bundle.putSerializable(BundleKeys.KEY_OPERATION_CODE, operation)
router.pushController(
@ -261,20 +246,6 @@ class EntryMenuController(args: Bundle) :
.pushChangeHandler(HorizontalChangeHandler())
.popChangeHandler(HorizontalChangeHandler())
)
} else if (operation === ConversationOperationEnum.OPS_CODE_SHARE_LINK && activity != null) {
shareIntent?.putExtra(
Intent.EXTRA_TEXT,
ShareUtils.getStringForIntent(
activity,
binding.textEdit.text.toString(),
userManager,
conversation
)
)
val intent = Intent(shareIntent)
intent.component = ComponentName(packageName, name)
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK
activity?.startActivity(intent)
} else if (operation !== ConversationOperationEnum.OPS_CODE_INVITE_USERS) {
val bundle = Bundle()
bundle.putSerializable(BundleKeys.KEY_OPERATION_CODE, operation)
@ -336,10 +307,7 @@ class EntryMenuController(args: Bundle) :
companion object {
private val PASSWORD_ENTRY_OPERATIONS: List<ConversationOperationEnum> =
immutableListOf(
ConversationOperationEnum.OPS_CODE_JOIN_ROOM,
ConversationOperationEnum.OPS_CODE_CHANGE_PASSWORD,
ConversationOperationEnum.OPS_CODE_SET_PASSWORD,
ConversationOperationEnum.OPS_CODE_SHARE_LINK
ConversationOperationEnum.OPS_CODE_JOIN_ROOM
)
const val OPACITY_DISABLED = 0.38f
const val OPACITY_BUTTON_DISABLED = 0.7f

View file

@ -211,11 +211,6 @@ class OperationsMenuController(args: Bundle) : BaseController(
credentials = ApiUtils.getCredentials(currentUser!!.username, currentUser!!.token)
when (operation) {
ConversationOperationEnum.OPS_CODE_RENAME_ROOM -> operationRenameRoom()
ConversationOperationEnum.OPS_CODE_MAKE_PUBLIC -> operationMakePublic()
ConversationOperationEnum.OPS_CODE_CHANGE_PASSWORD,
ConversationOperationEnum.OPS_CODE_CLEAR_PASSWORD,
ConversationOperationEnum.OPS_CODE_SET_PASSWORD -> operationChangePassword()
ConversationOperationEnum.OPS_CODE_MAKE_PRIVATE -> operationMakePrivate()
ConversationOperationEnum.OPS_CODE_GET_AND_JOIN_ROOM -> operationGetAndJoinRoom()
ConversationOperationEnum.OPS_CODE_INVITE_USERS -> operationInviteUsers()
ConversationOperationEnum.OPS_CODE_MARK_AS_READ -> operationMarkAsRead()
@ -267,56 +262,6 @@ class OperationsMenuController(args: Bundle) : BaseController(
.subscribe(GenericOperationsObserver())
}
private fun operationMakePrivate() {
ncApi.makeRoomPrivate(
credentials,
ApiUtils.getUrlForRoomPublic(
apiVersion(),
currentUser!!.baseUrl,
conversation!!.token
)
)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.retry(1)
.subscribe(GenericOperationsObserver())
}
private fun operationChangePassword() {
var pass: String? = ""
if (conversation!!.password != null) {
pass = conversation!!.password
}
ncApi.setPassword(
credentials,
ApiUtils.getUrlForRoomPassword(
apiVersion(),
currentUser!!.baseUrl,
conversation!!.token
),
pass
)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.retry(1)
.subscribe(GenericOperationsObserver())
}
private fun operationMakePublic() {
ncApi.makeRoomPublic(
credentials,
ApiUtils.getUrlForRoomPublic(
apiVersion(),
currentUser!!.baseUrl,
conversation!!.token
)
)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.retry(1)
.subscribe(GenericOperationsObserver())
}
private fun operationRenameRoom() {
ncApi.renameRoom(
credentials,

View file

@ -0,0 +1,251 @@
package com.nextcloud.talk.conversation.info
import android.annotation.SuppressLint
import android.content.Intent
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.widget.Toast
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.widget.SwitchCompat
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.nextcloud.talk.R
import com.nextcloud.talk.controllers.ConversationInfoController
import com.nextcloud.talk.data.user.model.User
import com.nextcloud.talk.databinding.ControllerConversationInfoBinding
import com.nextcloud.talk.databinding.DialogPasswordBinding
import com.nextcloud.talk.models.json.conversations.Conversation
import com.nextcloud.talk.repositories.conversations.ConversationsRepository
import com.nextcloud.talk.utils.Mimetype
import com.nextcloud.talk.utils.ShareUtils
import io.reactivex.Observer
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.disposables.Disposable
import io.reactivex.schedulers.Schedulers
class GuestAccessHelper(
controller: ConversationInfoController,
private val binding: ControllerConversationInfoBinding,
private val conversation: Conversation,
private val conversationUser: User
) {
private val activity = controller.activity!!
private val conversationsRepository = controller.conversationsRepository
private val viewThemeUtils = controller.viewThemeUtils
private val context = controller.context
fun setupGuestAccess() {
val guestAccessAllowSwitch = (
binding.guestAccessView.guestAccessAllowSwitch.findViewById<View>(R.id.mp_checkable)
as SwitchCompat
)
val guestAccessPasswordSwitch = (
binding.guestAccessView.guestAccessPasswordSwitch.findViewById<View>(R.id.mp_checkable)
as SwitchCompat
)
if (conversation.canModerate(conversationUser)) {
binding.guestAccessView.guestAccessSettings.visibility = View.VISIBLE
} else {
return
}
if (conversation.type == Conversation.ConversationType.ROOM_PUBLIC_CALL) {
guestAccessAllowSwitch.isChecked = true
showAllOptions()
if (conversation.hasPassword) {
guestAccessPasswordSwitch.isChecked = true
}
}
binding.guestAccessView.guestAccessAllowSwitch.setOnClickListener {
conversationsRepository.allowGuests(
conversation.token!!,
!guestAccessAllowSwitch.isChecked
).subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread()).subscribe(AllowGuestsResultObserver())
}
binding.guestAccessView.guestAccessPasswordSwitch.setOnClickListener {
if (guestAccessPasswordSwitch.isChecked) {
conversationsRepository.password("", conversation.token!!).subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread()).subscribe(PasswordResultObserver(false))
} else {
showPasswordDialog(guestAccessPasswordSwitch)
}
}
binding.guestAccessView.guestAccessCopyUrl.setOnClickListener {
shareUrl()
}
binding.guestAccessView.guestAccessResendInvitations.setOnClickListener {
conversationsRepository.resendInvitations(conversation.token!!).subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread()).subscribe(ResendInvitationsObserver())
}
}
@SuppressLint("InflateParams")
private fun showPasswordDialog(guestAccessPasswordSwitch: SwitchCompat) {
val builder = MaterialAlertDialogBuilder(activity)
builder.apply {
val dialogPassword = DialogPasswordBinding.inflate(LayoutInflater.from(context))
viewThemeUtils.colorEditText(dialogPassword.password)
setView(dialogPassword.root)
setTitle(R.string.nc_guest_access_password_dialog_title)
setPositiveButton(R.string.nc_ok) { _, _ ->
val password = dialogPassword.password.text.toString()
conversationsRepository.password(password, conversation.token!!)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(PasswordResultObserver(true))
}
setNegativeButton(R.string.nc_cancel) { _, _ ->
guestAccessPasswordSwitch.isChecked = false
}
}
createDialog(builder)
}
private fun createDialog(builder: MaterialAlertDialogBuilder) {
builder.create()
viewThemeUtils.colorMaterialAlertDialogBackground(binding.conversationInfoName.context, builder)
val dialog = builder.show()
viewThemeUtils.colorTextButtons(
dialog.getButton(AlertDialog.BUTTON_POSITIVE),
dialog.getButton(AlertDialog.BUTTON_NEGATIVE)
)
}
private fun shareUrl() {
val sendIntent: Intent = Intent().apply {
action = Intent.ACTION_SEND
type = Mimetype.TEXT_PLAIN
putExtra(
Intent.EXTRA_SUBJECT,
String.format(
activity.resources.getString(R.string.nc_share_subject),
activity.resources.getString(R.string.nc_app_product_name)
)
)
putExtra(
Intent.EXTRA_TEXT,
ShareUtils.getStringForIntent(activity, conversationUser, conversation)
)
}
val shareIntent = Intent.createChooser(sendIntent, null)
activity.startActivity(shareIntent)
}
inner class ResendInvitationsObserver : Observer<ConversationsRepository.ResendInvitationsResult> {
private lateinit var resendInvitationsResult: ConversationsRepository.ResendInvitationsResult
override fun onSubscribe(d: Disposable) = Unit
override fun onNext(t: ConversationsRepository.ResendInvitationsResult) {
resendInvitationsResult = t
}
override fun onError(e: Throwable) {
val message = context.getString(R.string.nc_guest_access_resend_invitations_failed)
Toast.makeText(context, message, Toast.LENGTH_SHORT).show()
Log.e(TAG, message, e)
}
override fun onComplete() {
if (resendInvitationsResult.successful) {
Toast.makeText(context, R.string.nc_guest_access_resend_invitations_successful, Toast.LENGTH_SHORT)
.show()
}
}
}
inner class AllowGuestsResultObserver : Observer<ConversationsRepository.AllowGuestsResult> {
private lateinit var allowGuestsResult: ConversationsRepository.AllowGuestsResult
override fun onNext(t: ConversationsRepository.AllowGuestsResult) {
allowGuestsResult = t
}
override fun onError(e: Throwable) {
val message = context.getString(R.string.nc_guest_access_allow_failed)
Toast.makeText(context, message, Toast.LENGTH_LONG).show()
Log.e(TAG, message, e)
}
override fun onComplete() {
(
binding.guestAccessView.guestAccessAllowSwitch.findViewById<View>(R.id.mp_checkable)
as SwitchCompat
).isChecked = allowGuestsResult.allow
if (allowGuestsResult.allow) {
showAllOptions()
} else {
hideAllOptions()
}
}
override fun onSubscribe(d: Disposable) = Unit
}
private fun showAllOptions() {
binding.guestAccessView.guestAccessPasswordSwitch.visibility = View.VISIBLE
binding.guestAccessView.guestAccessCopyUrl.visibility = View.VISIBLE
if (conversationUser.capabilities?.spreedCapability?.features?.contains("sip-support") == true) {
binding.guestAccessView.guestAccessResendInvitations.visibility = View.VISIBLE
}
}
private fun hideAllOptions() {
binding.guestAccessView.guestAccessPasswordSwitch.visibility = View.GONE
binding.guestAccessView.guestAccessCopyUrl.visibility = View.GONE
binding.guestAccessView.guestAccessResendInvitations.visibility = View.GONE
}
inner class PasswordResultObserver(private val setPassword: Boolean) :
Observer<ConversationsRepository.PasswordResult> {
private lateinit var passwordResult: ConversationsRepository.PasswordResult
override fun onSubscribe(d: Disposable) = Unit
override fun onNext(t: ConversationsRepository.PasswordResult) {
passwordResult = t
}
override fun onError(e: Throwable) {
val message = context.getString(R.string.nc_guest_access_password_failed)
Toast.makeText(context, message, Toast.LENGTH_LONG).show()
Log.e(TAG, message, e)
}
override fun onComplete() {
val guestAccessPasswordSwitch = (
binding.guestAccessView.guestAccessPasswordSwitch.findViewById<View>(R.id.mp_checkable)
as SwitchCompat
)
guestAccessPasswordSwitch.isChecked = passwordResult.passwordSet && setPassword
if (passwordResult.passwordIsWeak) {
val builder = MaterialAlertDialogBuilder(activity)
builder.apply {
setTitle(R.string.nc_guest_access_password_weak_alert_title)
setMessage(passwordResult.message)
setPositiveButton("OK") { _, _ -> }
}
createDialog(builder)
}
}
}
companion object {
private val TAG = GuestAccessHelper::class.simpleName
}
}

View file

@ -33,6 +33,8 @@ import com.nextcloud.talk.polls.repositories.PollRepository
import com.nextcloud.talk.polls.repositories.PollRepositoryImpl
import com.nextcloud.talk.remotefilebrowser.repositories.RemoteFileBrowserItemsRepository
import com.nextcloud.talk.remotefilebrowser.repositories.RemoteFileBrowserItemsRepositoryImpl
import com.nextcloud.talk.repositories.conversations.ConversationsRepository
import com.nextcloud.talk.repositories.conversations.ConversationsRepositoryImpl
import com.nextcloud.talk.repositories.unifiedsearch.UnifiedSearchRepository
import com.nextcloud.talk.repositories.unifiedsearch.UnifiedSearchRepositoryImpl
import com.nextcloud.talk.shareditems.repositories.SharedItemsRepository
@ -44,6 +46,12 @@ import okhttp3.OkHttpClient
@Module
class RepositoryModule {
@Provides
fun provideConversationsRepository(ncApi: NcApi, userProvider: CurrentUserProviderNew): ConversationsRepository {
return ConversationsRepositoryImpl(ncApi, userProvider)
}
@Provides
fun provideSharedItemsRepository(ncApi: NcApi): SharedItemsRepository {
return SharedItemsRepositoryImpl(ncApi)

View file

@ -0,0 +1,36 @@
/*
* Nextcloud Talk application
*
* @author Tim Krüger
* Copyright (C) 2022 Tim Krüger
* Copyright (C) 2022 Nextcloud GmbH
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.nextcloud.talk.models.json.conversations.password
import android.os.Parcelable
import com.bluelinelabs.logansquare.annotation.JsonField
import com.bluelinelabs.logansquare.annotation.JsonObject
import kotlinx.android.parcel.Parcelize
@JsonObject
@Parcelize
data class PasswordData(
@JsonField(name = ["message"])
var message: String? = null
) : Parcelable {
// This constructor is added to work with the 'com.bluelinelabs.logansquare.annotation.JsonObject'
constructor() : this(null)
}

View file

@ -0,0 +1,40 @@
/*
* Nextcloud Talk application
*
* @author Tim Krüger
* Copyright (C) 2022 Tim Krüger
* Copyright (C) 2022 Nextcloud GmbH
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.nextcloud.talk.models.json.conversations.password
import android.os.Parcelable
import com.bluelinelabs.logansquare.annotation.JsonField
import com.bluelinelabs.logansquare.annotation.JsonObject
import com.nextcloud.talk.models.json.generic.GenericMeta
import kotlinx.android.parcel.Parcelize
@Parcelize
@JsonObject
data class PasswordOCS(
@JsonField(name = ["meta"])
var meta: GenericMeta? = null,
@JsonField(name = ["data"])
var data: PasswordData? = null
) : Parcelable {
// This constructor is added to work with the 'com.bluelinelabs.logansquare.annotation.JsonObject'
constructor() : this(null, null)
}

View file

@ -0,0 +1,36 @@
/*
* Nextcloud Talk application
*
* @author Tim Krüger
* Copyright (C) 2022 Tim Krüger
* Copyright (C) 2022 Nextcloud GmbH
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.nextcloud.talk.models.json.conversations.password
import android.os.Parcelable
import com.bluelinelabs.logansquare.annotation.JsonField
import com.bluelinelabs.logansquare.annotation.JsonObject
import kotlinx.android.parcel.Parcelize
@Parcelize
@JsonObject
data class PasswordOverall(
@JsonField(name = ["ocs"])
var ocs: PasswordOCS? = null
) : Parcelable {
// This constructor is added to work with the 'com.bluelinelabs.logansquare.annotation.JsonObject'
constructor() : this(null)
}

View file

@ -0,0 +1,46 @@
/*
* Nextcloud Talk application
*
* @author Tim Krüger
* Copyright (C) 2022 Tim Krüger
* Copyright (C) 2022 Nextcloud GmbH
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.nextcloud.talk.repositories.conversations
import io.reactivex.Observable
interface ConversationsRepository {
data class AllowGuestsResult(
val allow: Boolean
)
fun allowGuests(token: String, allow: Boolean): Observable<AllowGuestsResult>
data class PasswordResult(
val passwordSet: Boolean,
val passwordIsWeak: Boolean,
val message: String
)
fun password(password: String, token: String): Observable<PasswordResult>
data class ResendInvitationsResult(
val successful: Boolean
)
fun resendInvitations(token: String): Observable<ResendInvitationsResult>
}

View file

@ -0,0 +1,113 @@
/*
* Nextcloud Talk application
*
* @author Tim Krüger
* Copyright (C) 2022 Tim Krüger
* Copyright (C) 2022 Nextcloud GmbH
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.nextcloud.talk.repositories.conversations
import com.bluelinelabs.logansquare.LoganSquare
import com.nextcloud.talk.api.NcApi
import com.nextcloud.talk.data.user.model.User
import com.nextcloud.talk.models.json.conversations.password.PasswordOverall
import com.nextcloud.talk.repositories.conversations.ConversationsRepository.AllowGuestsResult
import com.nextcloud.talk.repositories.conversations.ConversationsRepository.PasswordResult
import com.nextcloud.talk.repositories.conversations.ConversationsRepository.ResendInvitationsResult
import com.nextcloud.talk.utils.ApiUtils
import com.nextcloud.talk.utils.database.user.CurrentUserProviderNew
import io.reactivex.Observable
class ConversationsRepositoryImpl(private val api: NcApi, private val userProvider: CurrentUserProviderNew) :
ConversationsRepository {
private val user: User
get() = userProvider.currentUser.blockingGet()
private val credentials: String
get() = ApiUtils.getCredentials(user.username, user.token)
override fun allowGuests(token: String, allow: Boolean): Observable<AllowGuestsResult> {
val url = ApiUtils.getUrlForRoomPublic(
apiVersion(),
user.baseUrl,
token
)
val apiObservable = if (allow) {
api.makeRoomPublic(
credentials,
url
)
} else {
api.makeRoomPrivate(
credentials,
url
)
}
return apiObservable.map { AllowGuestsResult(it.ocs!!.meta!!.statusCode == STATUS_CODE_OK && allow) }
}
override fun password(password: String, token: String): Observable<PasswordResult> {
val apiObservable = api.setPassword2(
credentials,
ApiUtils.getUrlForRoomPassword(
apiVersion(),
user.baseUrl!!,
token
),
password
)
return apiObservable.map {
val passwordPolicyMessage = if (it.code() == STATUS_CODE_BAD_REQUEST) {
LoganSquare.parse(it.errorBody()!!.string(), PasswordOverall::class.java).ocs!!.data!!
.message!!
} else {
""
}
PasswordResult(it.isSuccessful, passwordPolicyMessage.isNotEmpty(), passwordPolicyMessage)
}
}
override fun resendInvitations(token: String): Observable<ResendInvitationsResult> {
val apiObservable = api.resendParticipantInvitations(
credentials,
ApiUtils.getUrlForParticipantsResendInvitations(
apiVersion(),
user.baseUrl!!,
token
)
)
return apiObservable.map {
ResendInvitationsResult(true)
}
}
private fun apiVersion(): Int {
return ApiUtils.getConversationApiVersion(user, intArrayOf(ApiUtils.APIv4))
}
companion object {
const val STATUS_CODE_OK = 200
const val STATUS_CODE_BAD_REQUEST = 400
}
}

View file

@ -21,7 +21,6 @@
package com.nextcloud.talk.ui.dialog
import android.app.Activity
import android.content.Intent
import android.os.Bundle
import android.text.TextUtils
import android.view.View
@ -42,14 +41,9 @@ import com.nextcloud.talk.application.NextcloudTalkApplication
import com.nextcloud.talk.controllers.ConversationsListController
import com.nextcloud.talk.controllers.bottomsheet.ConversationOperationEnum
import com.nextcloud.talk.controllers.bottomsheet.ConversationOperationEnum.OPS_CODE_ADD_FAVORITE
import com.nextcloud.talk.controllers.bottomsheet.ConversationOperationEnum.OPS_CODE_CHANGE_PASSWORD
import com.nextcloud.talk.controllers.bottomsheet.ConversationOperationEnum.OPS_CODE_CLEAR_PASSWORD
import com.nextcloud.talk.controllers.bottomsheet.ConversationOperationEnum.OPS_CODE_MAKE_PRIVATE
import com.nextcloud.talk.controllers.bottomsheet.ConversationOperationEnum.OPS_CODE_MAKE_PUBLIC
import com.nextcloud.talk.controllers.bottomsheet.ConversationOperationEnum.OPS_CODE_MARK_AS_READ
import com.nextcloud.talk.controllers.bottomsheet.ConversationOperationEnum.OPS_CODE_REMOVE_FAVORITE
import com.nextcloud.talk.controllers.bottomsheet.ConversationOperationEnum.OPS_CODE_RENAME_ROOM
import com.nextcloud.talk.controllers.bottomsheet.ConversationOperationEnum.OPS_CODE_SET_PASSWORD
import com.nextcloud.talk.controllers.bottomsheet.EntryMenuController
import com.nextcloud.talk.controllers.bottomsheet.OperationsMenuController
import com.nextcloud.talk.data.user.model.User
@ -58,8 +52,6 @@ import com.nextcloud.talk.jobs.LeaveConversationWorker
import com.nextcloud.talk.models.json.conversations.Conversation
import com.nextcloud.talk.ui.theme.ViewThemeUtils
import com.nextcloud.talk.users.UserManager
import com.nextcloud.talk.utils.Mimetype.TEXT_PLAIN
import com.nextcloud.talk.utils.ShareUtils
import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_INTERNAL_USER_ID
import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_OPERATION_CODE
import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_ROOM
@ -130,34 +122,10 @@ class ConversationsListBottomDialog(
conversation.isNameEditable(currentUser)
)
binding.conversationOperationMakePublic.visibility = setVisibleIf(
canModerate && !conversation.isPublic
)
binding.conversationOperationChangePassword.visibility = setVisibleIf(
canModerate && conversation.hasPassword && conversation.isPublic
)
binding.conversationOperationClearPassword.visibility = setVisibleIf(
canModerate && conversation.hasPassword && conversation.isPublic
)
binding.conversationOperationSetPassword.visibility = setVisibleIf(
canModerate && !conversation.hasPassword && conversation.isPublic
)
binding.conversationOperationDelete.visibility = setVisibleIf(
canModerate
)
binding.conversationOperationShareLink.visibility = setVisibleIf(
conversation.isPublic
)
binding.conversationOperationMakePrivate.visibility = setVisibleIf(
conversation.isPublic && canModerate
)
binding.conversationOperationLeave.visibility = setVisibleIf(
conversation.canLeave() &&
// leaving is by api not possible for the last user with moderator permissions.
@ -210,26 +178,6 @@ class ConversationsListBottomDialog(
dismiss()
}
binding.conversationOperationMakePublic.setOnClickListener {
executeOperationsMenuController(OPS_CODE_MAKE_PUBLIC)
}
binding.conversationOperationMakePrivate.setOnClickListener {
executeOperationsMenuController(OPS_CODE_MAKE_PRIVATE)
}
binding.conversationOperationChangePassword.setOnClickListener {
executeEntryMenuController(OPS_CODE_CHANGE_PASSWORD)
}
binding.conversationOperationClearPassword.setOnClickListener {
executeOperationsMenuController(OPS_CODE_CLEAR_PASSWORD)
}
binding.conversationOperationSetPassword.setOnClickListener {
executeEntryMenuController(OPS_CODE_SET_PASSWORD)
}
binding.conversationOperationRename.setOnClickListener {
executeEntryMenuController(OPS_CODE_RENAME_ROOM)
}
@ -237,30 +185,6 @@ class ConversationsListBottomDialog(
binding.conversationOperationMarkAsRead.setOnClickListener {
executeOperationsMenuController(OPS_CODE_MARK_AS_READ)
}
binding.conversationOperationShareLink.setOnClickListener {
val sendIntent: Intent = Intent().apply {
action = Intent.ACTION_SEND
type = TEXT_PLAIN
putExtra(
Intent.EXTRA_SUBJECT,
String.format(
activity.resources.getString(R.string.nc_share_subject),
activity.resources.getString(R.string.nc_app_product_name)
)
)
// password should not be shared!!
putExtra(
Intent.EXTRA_TEXT,
ShareUtils.getStringForIntent(activity, null, userManager, conversation)
)
}
val shareIntent = Intent.createChooser(sendIntent, null)
activity.startActivity(shareIntent)
dismiss()
}
}
private fun executeOperationsMenuController(operation: ConversationOperationEnum) {

View file

@ -218,6 +218,10 @@ public class ApiUtils {
return getUrlForParticipants(version, baseUrl, token) + "/self";
}
public static String getUrlForParticipantsResendInvitations(int version, String baseUrl, String token) {
return getUrlForParticipants(version, baseUrl, token) + "/resend-invitations";
}
public static String getUrlForRoomFavorite(int version, String baseUrl, String token) {
return getUrlForRoom(version, baseUrl, token) + "/favorite";
}

View file

@ -21,28 +21,20 @@ package com.nextcloud.talk.utils
import android.content.Context
import com.nextcloud.talk.R
import com.nextcloud.talk.data.user.model.User
import com.nextcloud.talk.models.json.conversations.Conversation
import com.nextcloud.talk.users.UserManager
object ShareUtils {
fun getStringForIntent(
context: Context?,
password: String?,
userManager: UserManager,
context: Context,
user: User,
conversation: Conversation?
): String {
val userEntity = userManager.currentUser.blockingGet()
var shareString = ""
if (userEntity != null && context != null) {
shareString = String.format(
context.resources.getString(R.string.nc_share_text),
userEntity.baseUrl,
conversation?.token
)
if (!password.isNullOrEmpty()) {
shareString += String.format(context.resources.getString(R.string.nc_share_text_pass), password)
}
}
return shareString
return String.format(
context.resources.getString(R.string.nc_share_text),
user.baseUrl,
conversation?.token
)
}
}

View file

@ -4,6 +4,8 @@
~ @author Mario Danic
~ @author Andy Scherzinger
~ @author Marcel Hibbe
~ @author Tim Krüger
~ Copyright (C) 2022 Tim Krüger <t@timkrueger.me>
~ Copyright (C) 2022 Marcel Hibbe <dev@mhibbe.de>
~ Copyright (C) 2021 Andy Scherzinger <info@andy-scherzinger.de>
~ Copyright (C) 2017-2018 Mario Danic <mario@lovelyhq.com>
@ -25,10 +27,10 @@
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:apc="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
tools:background="@color/white"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
android:orientation="vertical"
tools:background="@color/white">
<ProgressBar
android:id="@+id/progressBar"
@ -80,8 +82,8 @@
android:layout_width="@dimen/avatar_size_big"
android:layout_height="@dimen/avatar_size_big"
android:layout_centerHorizontal="true"
tools:background="@color/hwSecurityRed"
apc:roundAsCircle="true" />
apc:roundAsCircle="true"
tools:background="@color/hwSecurityRed" />
</RelativeLayout>
</com.yarolegovich.mp.MaterialPreferenceCategory>
@ -150,6 +152,14 @@
android:visibility="gone"
tools:visibility="visible" />
<include
android:id="@+id/guest_access_view"
layout="@layout/guest_access_settings_item"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:visibility="gone"
tools:visibility="visible" />
</LinearLayout>
<com.yarolegovich.mp.MaterialPreferenceCategory

View file

@ -166,126 +166,6 @@
android:textSize="@dimen/bottom_sheet_text_size" />
</LinearLayout>
<LinearLayout
android:id="@+id/conversation_operation_make_public"
android:layout_width="match_parent"
android:layout_height="@dimen/bottom_sheet_item_height"
android:background="?android:attr/selectableItemBackground"
android:gravity="center_vertical"
android:orientation="horizontal"
android:paddingStart="@dimen/standard_padding"
android:paddingEnd="@dimen/standard_padding"
tools:ignore="UseCompoundDrawables">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:contentDescription="@null"
android:src="@drawable/ic_link_grey600_24px"
app:tint="@color/high_emphasis_menu_icon" />
<androidx.appcompat.widget.AppCompatTextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="start|center_vertical"
android:paddingStart="40dp"
android:paddingEnd="@dimen/zero"
android:text="@string/nc_make_call_public"
android:textAlignment="viewStart"
android:textColor="@color/high_emphasis_text"
android:textSize="@dimen/bottom_sheet_text_size" />
</LinearLayout>
<LinearLayout
android:id="@+id/conversation_operation_change_password"
android:layout_width="match_parent"
android:layout_height="@dimen/bottom_sheet_item_height"
android:background="?android:attr/selectableItemBackground"
android:gravity="center_vertical"
android:orientation="horizontal"
android:paddingStart="@dimen/standard_padding"
android:paddingEnd="@dimen/standard_padding"
tools:ignore="UseCompoundDrawables">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:contentDescription="@null"
android:src="@drawable/ic_lock_grey600_24px"
app:tint="@color/high_emphasis_menu_icon" />
<androidx.appcompat.widget.AppCompatTextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="start|center_vertical"
android:paddingStart="40dp"
android:paddingEnd="@dimen/zero"
android:text="@string/nc_change_password"
android:textAlignment="viewStart"
android:textColor="@color/high_emphasis_text"
android:textSize="@dimen/bottom_sheet_text_size" />
</LinearLayout>
<LinearLayout
android:id="@+id/conversation_operation_clear_password"
android:layout_width="match_parent"
android:layout_height="@dimen/bottom_sheet_item_height"
android:background="?android:attr/selectableItemBackground"
android:gravity="center_vertical"
android:orientation="horizontal"
android:paddingStart="@dimen/standard_padding"
android:paddingEnd="@dimen/standard_padding"
tools:ignore="UseCompoundDrawables">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:contentDescription="@null"
android:src="@drawable/ic_lock_open_grey600_24dp"
app:tint="@color/high_emphasis_menu_icon" />
<androidx.appcompat.widget.AppCompatTextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="start|center_vertical"
android:paddingStart="40dp"
android:paddingEnd="@dimen/zero"
android:text="@string/nc_clear_password"
android:textAlignment="viewStart"
android:textColor="@color/high_emphasis_text"
android:textSize="@dimen/bottom_sheet_text_size" />
</LinearLayout>
<LinearLayout
android:id="@+id/conversation_operation_set_password"
android:layout_width="match_parent"
android:layout_height="@dimen/bottom_sheet_item_height"
android:background="?android:attr/selectableItemBackground"
android:gravity="center_vertical"
android:orientation="horizontal"
android:paddingStart="@dimen/standard_padding"
android:paddingEnd="@dimen/standard_padding"
tools:ignore="UseCompoundDrawables">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:contentDescription="@null"
android:src="@drawable/ic_lock_plus_grey600_24dp"
app:tint="@color/high_emphasis_menu_icon" />
<androidx.appcompat.widget.AppCompatTextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="start|center_vertical"
android:paddingStart="40dp"
android:paddingEnd="@dimen/zero"
android:text="@string/nc_set_password"
android:textAlignment="viewStart"
android:textColor="@color/high_emphasis_text"
android:textSize="@dimen/bottom_sheet_text_size" />
</LinearLayout>
<LinearLayout
android:id="@+id/conversation_operation_delete"
android:layout_width="match_parent"
@ -316,66 +196,6 @@
android:textSize="@dimen/bottom_sheet_text_size" />
</LinearLayout>
<LinearLayout
android:id="@+id/conversation_operation_share_link"
android:layout_width="match_parent"
android:layout_height="@dimen/bottom_sheet_item_height"
android:background="?android:attr/selectableItemBackground"
android:gravity="center_vertical"
android:orientation="horizontal"
android:paddingStart="@dimen/standard_padding"
android:paddingEnd="@dimen/standard_padding"
tools:ignore="UseCompoundDrawables">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:contentDescription="@null"
android:src="@drawable/ic_link_grey600_24px"
app:tint="@color/high_emphasis_menu_icon" />
<androidx.appcompat.widget.AppCompatTextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="start|center_vertical"
android:paddingStart="40dp"
android:paddingEnd="@dimen/zero"
android:text="@string/nc_share_link"
android:textAlignment="viewStart"
android:textColor="@color/high_emphasis_text"
android:textSize="@dimen/bottom_sheet_text_size" />
</LinearLayout>
<LinearLayout
android:id="@+id/conversation_operation_make_private"
android:layout_width="match_parent"
android:layout_height="@dimen/bottom_sheet_item_height"
android:background="?android:attr/selectableItemBackground"
android:gravity="center_vertical"
android:orientation="horizontal"
android:paddingStart="@dimen/standard_padding"
android:paddingEnd="@dimen/standard_padding"
tools:ignore="UseCompoundDrawables">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:contentDescription="@null"
android:src="@drawable/ic_group_grey600_24px"
app:tint="@color/high_emphasis_menu_icon" />
<androidx.appcompat.widget.AppCompatTextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="start|center_vertical"
android:paddingStart="40dp"
android:paddingEnd="@dimen/zero"
android:text="@string/nc_make_call_private"
android:textAlignment="viewStart"
android:textColor="@color/high_emphasis_text"
android:textSize="@dimen/bottom_sheet_text_size" />
</LinearLayout>
<LinearLayout
android:id="@+id/conversation_operation_leave"
android:layout_width="match_parent"

View file

@ -0,0 +1,42 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Nextcloud Talk application
@author Andy Scherzinger
@author Tim Krüger
Copyright (C) 2022 Andy Scherzinger <info@andy-scherzinger.de>
Copyright (C) 2022 Tim Krüger
Copyright (C) 2022 Nextcloud GmbH
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
tools:background="@color/white">
<EditText
android:id="@+id/password"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/dialog_padding"
android:layout_marginTop="@dimen/standard_margin"
android:layout_marginEnd="@dimen/dialog_padding"
android:layout_marginBottom="@dimen/standard_margin"
android:hint="@string/nc_guest_access_password_dialog_hint"
android:inputType="textPassword"
android:importantForAutofill="no" />
</LinearLayout>

View file

@ -0,0 +1,81 @@
<?xml version="1.0" encoding="utf-8"?><!--
Nextcloud Talk application
@author Tim Krüger
Copyright (C) 2022 Tim Krüger
Copyright (C) 2022 Nextcloud GmbH
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
-->
<com.yarolegovich.mp.MaterialPreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:apc="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/guest_access_settings"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<com.yarolegovich.mp.MaterialPreferenceCategory
android:id="@+id/guest_access_category"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:animateLayoutChanges="true"
apc:cardBackgroundColor="@color/bg_default"
apc:cardElevation="0dp"
apc:mpc_title="@string/nc_guest_access">
<com.yarolegovich.mp.MaterialSwitchPreference
android:id="@+id/guest_access_allow_switch"
android:layout_width="match_parent"
android:layout_height="wrap_content"
apc:mp_default_value="false"
apc:mp_key="guest_access_allowed"
apc:mp_summary="@string/nc_guest_access_allow_summary"
apc:mp_title="@string/nc_guest_access_allow_title" />
<com.yarolegovich.mp.MaterialSwitchPreference
android:id="@+id/guest_access_password_switch"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:visibility="gone"
apc:mp_default_value="false"
apc:mp_key="guest_access_password"
apc:mp_summary="@string/nc_guest_access_password_summary"
apc:mp_title="@string/nc_guest_access_password_title"
tools:visibility="visible" />
<com.yarolegovich.mp.MaterialStandardPreference
android:id="@+id/guest_access_copy_url"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:visibility="gone"
apc:mp_icon="@drawable/ic_share_variant"
apc:mp_icon_tint="@color/grey_600"
apc:mp_title="@string/nc_guest_access_share_link"
tools:visibility="visible" >
</com.yarolegovich.mp.MaterialStandardPreference>
<com.yarolegovich.mp.MaterialStandardPreference
android:id="@+id/guest_access_resend_invitations"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="gone"
apc:mp_icon="@drawable/ic_email"
apc:mp_icon_tint="@color/grey_600"
apc:mp_title="@string/nc_guest_access_resend_invitations"
tools:visibility="visible" />
</com.yarolegovich.mp.MaterialPreferenceCategory>
</com.yarolegovich.mp.MaterialPreferenceScreen>

View file

@ -168,12 +168,6 @@
<string name="nc_clear_history_warning">Do you really want to delete all messages in this conversation?</string>
<string name="nc_clear_history_success">All messages were deleted</string>
<string name="nc_rename">Rename conversation</string>
<string name="nc_set_password">Set a password</string>
<string name="nc_change_password">Change password</string>
<string name="nc_clear_password">Clear password</string>
<string name="nc_share_link">Share link</string>
<string name="nc_make_call_public">Make conversation public</string>
<string name="nc_make_call_private">Make conversation private</string>
<string name="nc_delete_call">Delete conversation</string>
<string name="nc_delete">Delete</string>
<string name="nc_delete_all">Delete all</string>
@ -337,6 +331,22 @@
<string name="emoji_backspace">Backspace</string>
<string name="emoji_search">Search emoji</string>
<!-- Conversation info guest access -->
<string name="nc_guest_access">Guest access</string>
<string name="nc_guest_access_allow_title">Allow guests</string>
<string name="nc_guest_access_allow_summary">Allow guests to share a public link to join this conversation.</string>
<string name="nc_guest_access_allow_failed">Can\'t en-/disable guest access.</string>
<string name="nc_guest_access_password_title">Password protection</string>
<string name="nc_guest_access_password_summary">Set a password to restrict who can use the public link.</string>
<string name="nc_guest_access_password_dialog_title">Guest access password</string>
<string name="nc_guest_access_password_dialog_hint">Enter a password</string>
<string name="nc_guest_access_password_failed">Error during setting/disabling the password.</string>
<string name="nc_guest_access_password_weak_alert_title">Weak password</string>
<string name="nc_guest_access_share_link">Share conversation link</string>
<string name="nc_guest_access_resend_invitations">Resend invitations</string>
<string name="nc_guest_access_resend_invitations_successful">Invitations were sent out again.</string>
<string name="nc_guest_access_resend_invitations_failed">Invitations were not send due to an error.</string>
<!-- Content descriptions -->
<string name="nc_description_send_message_button">Send message</string>

View file

@ -72,20 +72,8 @@ class ShareUtilsTest {
)
Assert.assertEquals(
"Intent string was not as expected",
expectedResult, ShareUtils.getStringForIntent(context, "", userManager!!, conversation)
)
}
@Test
fun stringForIntent_passwordGiven_correctStringWithPasswordReturned() {
val password = "superSecret"
val expectedResult = String.format(
"Join the conversation at %s/index.php/call/%s\nPassword: %s",
baseUrl, token, password
)
Assert.assertEquals(
"Intent string was not as expected",
expectedResult, ShareUtils.getStringForIntent(context, password, userManager!!, conversation)
expectedResult,
ShareUtils.getStringForIntent(context!!, userManager!!.currentUser.blockingGet(), conversation)
)
}
}