From 6664c2baa7bbdcd298caa7f93d5455f817d419ff Mon Sep 17 00:00:00 2001 From: Anton Chekulaev Date: Fri, 14 Aug 2020 14:52:15 +1000 Subject: [PATCH] Edit closed group activitiy cleanup. --- res/layout/activity_edit_closed_group.xml | 14 +- res/layout/activity_settings.xml | 4 +- res/layout/view_conversation.xml | 2 +- res/layout/view_mention_candidate.xml | 2 +- .../conversation/ConversationActivity.java | 3 +- .../activities/CreateClosedGroupActivity.kt | 5 +- .../activities/EditClosedGroupActivity.kt | 181 ++++++++++-------- .../loki/activities/EditClosedGroupAdapter.kt | 48 ----- .../EditClosedGroupMembersAdapter.kt | 42 ++++ .../loki/activities/SelectContactsActivity.kt | 9 +- .../loki/activities/SelectContactsAdapter.kt | 10 +- .../loki/activities/SettingsActivity.kt | 8 +- .../securesms/loki/views/ConversationView.kt | 2 +- .../loki/views/MentionCandidateView.kt | 2 +- 14 files changed, 176 insertions(+), 156 deletions(-) delete mode 100644 src/org/thoughtcrime/securesms/loki/activities/EditClosedGroupAdapter.kt create mode 100644 src/org/thoughtcrime/securesms/loki/activities/EditClosedGroupMembersAdapter.kt diff --git a/res/layout/activity_edit_closed_group.xml b/res/layout/activity_edit_closed_group.xml index 06dc73c339..074b726fb6 100644 --- a/res/layout/activity_edit_closed_group.xml +++ b/res/layout/activity_edit_closed_group.xml @@ -11,7 +11,7 @@ android:orientation="vertical"> diff --git a/res/layout/activity_settings.xml b/res/layout/activity_settings.xml index bff6499952..df55e40f5e 100644 --- a/res/layout/activity_settings.xml +++ b/res/layout/activity_settings.xml @@ -77,7 +77,7 @@ android:layout_marginTop="@dimen/medium_spacing" /> > { - private var members = listOf() - set(value) { field = value; editClosedGroupAdapter.members = value } +const val EXTRA_GROUP_ID = "GROUP_ID" +const val REQ_CODE_ADD_USERS = 124 +const val LOADER_ID_MEMBERS = 0 + +class EditClosedGroupActivity : PassphraseRequiredActionBarActivity() { + + private lateinit var memberListAdapter: EditClosedGroupMembersAdapter + private val originalMembers = HashSet() + private val members = HashSet() +// private var adminMembers = HashSet() + private lateinit var groupID: String - private var membersToRemove = listOf() - private var membersToAdd = listOf() - private var admins = listOf() - private val originalName by lazy { DatabaseFactory.getGroupDatabase(this).getGroup(groupID).get().title } - private var nameHasChanged = false + private lateinit var originalName: String private lateinit var newGroupDisplayName: String + private var nameHasChanged = false - private val editClosedGroupAdapter by lazy { - val result = EditClosedGroupAdapter(this) - result.glide = GlideApp.with(this) - result.memberClickListener = this - result - } private var isEditingGroupName = false set(value) { field = value; handleIsEditingDisplayNameChanged() } - companion object { - @JvmField - public val GROUP_ID = "GROUP_ID" - } - // region Lifecycle override fun onCreate(savedInstanceState: Bundle?, isReady: Boolean) { super.onCreate(savedInstanceState, isReady) - groupID = intent.getStringExtra(GROUP_ID) setContentView(R.layout.activity_edit_closed_group) + + this.groupID = intent.getStringExtra(EXTRA_GROUP_ID) + this.originalName = DatabaseFactory.getGroupDatabase(this).getGroup(groupID).get().title + supportActionBar!!.title = resources.getString(R.string.activity_edit_closed_group_title) - displayNameContainer.setOnClickListener { showEditDisplayNameUI() } - displayNameTextView.text = originalName - cancelEditButton.setOnClickListener { cancelEditingDisplayName() } - saveEditButton.setOnClickListener { saveDisplayName() } - recyclerView.adapter = editClosedGroupAdapter + + txvGroupNameDisplay.text = originalName + ctnGroupNameSection.setOnClickListener { showEditDisplayNameUI() } + btnCancelGroupNameEdit.setOnClickListener { cancelEditingDisplayName() } + btnSaveGroupName.setOnClickListener { saveDisplayName() } + + addMembersClosedGroupButton.setOnClickListener { onAddMembersClick() } + + this.memberListAdapter = EditClosedGroupMembersAdapter( + this, + GlideApp.with(this), + this::onMemberClick + ) + recyclerView.adapter = this.memberListAdapter recyclerView.layoutManager = LinearLayoutManager(this) - addMembersClosedGroupButton.setOnClickListener { onAddMember() } - LoaderManager.getInstance(this).initLoader(0, null, this) + + // Setup member list loader. + LoaderManager.getInstance(this).initLoader(LOADER_ID_MEMBERS, null, object : LoaderManager.LoaderCallbacks> { + override fun onCreateLoader(id: Int, bundle: Bundle?): Loader> { + return EditClosedGroupLoader(groupID, this@EditClosedGroupActivity) + } + + override fun onLoadFinished(loader: Loader>, members: List) { + // We no longer need any subsequent loading events + // (they will occur on every activity resume) + LoaderManager.getInstance(this@EditClosedGroupActivity).destroyLoader(LOADER_ID_MEMBERS) + + originalMembers.clear() + originalMembers.addAll(members.toHashSet()) + updateMembers(originalMembers) + } + + override fun onLoaderReset(loader: Loader>) { + updateMembers(setOf()) + } + }) } override fun onCreateOptionsMenu(menu: Menu?): Boolean { @@ -77,20 +101,12 @@ class EditClosedGroupActivity : PassphraseRequiredActionBarActivity(), MemberCli // endregion // region Updating - override fun onCreateLoader(id: Int, bundle: Bundle?): Loader> { - return EditClosedGroupLoader(groupID, this) - } - override fun onLoadFinished(loader: Loader>, members: List) { - update(members) - } - override fun onLoaderReset(loader: Loader>) { - update(listOf()) - } - - private fun update(members: List) { - this.members = members + private fun updateMembers(members: Set) { + this.members.clear() + this.members.addAll(members) + this.memberListAdapter.setItems(members) mainContentContainer.visibility = if (members.isEmpty()) View.GONE else View.VISIBLE emptyStateContainer.visibility = if (members.isEmpty()) View.VISIBLE else View.GONE invalidateOptionsMenu() @@ -99,8 +115,7 @@ class EditClosedGroupActivity : PassphraseRequiredActionBarActivity(), MemberCli // region Interaction override fun onOptionsItemSelected(item: MenuItem): Boolean { - val id = item.itemId - when(id) { + when(item.itemId) { R.id.applyButton -> modifyClosedGroup() } return super.onOptionsItemSelected(item) @@ -113,54 +128,54 @@ class EditClosedGroupActivity : PassphraseRequiredActionBarActivity(), MemberCli isEditingGroupName = false } - override fun onMemberClick(member: String) { + private fun onMemberClick(member: String) { val bottomSheet = GroupEditingOptionsBottomSheet() bottomSheet.onRemoveTapped = { - membersToRemove = membersToRemove + member - members = members - member + val changedMembers = members - member + updateMembers(changedMembers) bottomSheet.dismiss() } // bottomSheet.onAdminTapped = { // bottomSheet.dismiss() // } - bottomSheet.show(supportFragmentManager, "closeBottomSheet") + bottomSheet.show(supportFragmentManager, "MEMBER_BOTTOM_SHEET") } - private fun onAddMember() { + private fun onAddMembersClick() { val intent = Intent(this@EditClosedGroupActivity, SelectContactsActivity::class.java) - startActivityForResult(intent, 124) + startActivityForResult(intent, REQ_CODE_ADD_USERS) } override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { super.onActivityResult(requestCode, resultCode, data) - if (resultCode == RESULT_OK) { - if (data != null && data.hasExtra("Selected Contacts Result")) { - val returnedContacts = data.extras.getStringArray("Selected Contacts Result") - val selectedContacts = returnedContacts.toList() - membersToAdd = selectedContacts + membersToAdd - members = members + membersToAdd - Toast.makeText(this.applicationContext, membersToAdd.toString(), Toast.LENGTH_LONG).show() - //TODO:this works for users that were already in the group but were removed in the same edit activity, but not users that were never in the chat. WHy? + when (requestCode) { + REQ_CODE_ADD_USERS -> { + if (resultCode != RESULT_OK) return + if (data == null || data.extras == null || !data.hasExtra(EXTRA_SELECTED_CONTACTS)) return + + val selectedContacts = data.extras!!.getStringArray(EXTRA_SELECTED_CONTACTS)!!.toSet() + val changedMembers = members + selectedContacts + updateMembers(changedMembers) } } } private fun handleIsEditingDisplayNameChanged() { - cancelEditButton.visibility = if (isEditingGroupName) View.VISIBLE else View.GONE - saveEditButton.visibility = if (isEditingGroupName) View.VISIBLE else View.GONE - displayNameTextView.visibility = if (isEditingGroupName) View.INVISIBLE else View.VISIBLE - groupNameEditText.visibility = if (isEditingGroupName) View.VISIBLE else View.INVISIBLE + btnCancelGroupNameEdit.visibility = if (isEditingGroupName) View.VISIBLE else View.GONE + btnSaveGroupName.visibility = if (isEditingGroupName) View.VISIBLE else View.GONE + txvGroupNameDisplay.visibility = if (isEditingGroupName) View.INVISIBLE else View.VISIBLE + edtGroupName.visibility = if (isEditingGroupName) View.VISIBLE else View.INVISIBLE val inputMethodManager = getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager if (isEditingGroupName) { - groupNameEditText.requestFocus() - inputMethodManager.showSoftInput(groupNameEditText, 0) + edtGroupName.requestFocus() + inputMethodManager.showSoftInput(edtGroupName, 0) } else { - inputMethodManager.hideSoftInputFromWindow(groupNameEditText.windowToken, 0) + inputMethodManager.hideSoftInputFromWindow(edtGroupName.windowToken, 0) } } private fun saveDisplayName() { - var groupDisplayName = groupNameEditText.text.toString().trim() + val groupDisplayName = edtGroupName.text.toString().trim() if (groupDisplayName.isEmpty()) { return Toast.makeText(this, R.string.activity_settings_display_name_missing_error, Toast.LENGTH_SHORT).show() } @@ -171,26 +186,32 @@ class EditClosedGroupActivity : PassphraseRequiredActionBarActivity(), MemberCli return Toast.makeText(this, R.string.activity_settings_display_name_too_long_error, Toast.LENGTH_SHORT).show() } newGroupDisplayName = groupDisplayName - displayNameTextView.text = groupDisplayName + txvGroupNameDisplay.text = groupDisplayName nameHasChanged = true isEditingGroupName = false - return Toast.makeText(this, "Name Changed", Toast.LENGTH_SHORT).show() } private fun modifyClosedGroup() { - if (!nameHasChanged && membersToRemove.isEmpty() && membersToAdd.isEmpty()) { finish() } else { - var groupDisplayName = originalName - if (nameHasChanged) { - groupDisplayName = newGroupDisplayName - } - val finalGroupMembers = members.map { - Recipient.from(this, Address.fromSerialized(it), false) - }.toSet() - val finalGroupAdmins = admins.map { - Recipient.from(this, Address.fromSerialized(it), false) - }.toSet() - GroupManager.updateGroup(this, groupID, finalGroupMembers, null, groupDisplayName, finalGroupAdmins) + val membersHaveChanged = members.size != originalMembers.size || !members.containsAll(originalMembers) + + if (!nameHasChanged && !membersHaveChanged) { finish() + return } + + var groupDisplayName = originalName + if (nameHasChanged) { + groupDisplayName = newGroupDisplayName + } + + val finalGroupMembers = members.map { + Recipient.from(this, Address.fromSerialized(it), false) + }.toSet() +// val finalGroupAdmins = adminMembers.map { +// Recipient.from(this, Address.fromSerialized(it), false) +// }.toSet() + val finalGroupAdmins = finalGroupMembers.toSet() //TODO For now, consider all the group's users are admins. + GroupManager.updateGroup(this, groupID, finalGroupMembers, null, groupDisplayName, finalGroupAdmins) + finish() } } \ No newline at end of file diff --git a/src/org/thoughtcrime/securesms/loki/activities/EditClosedGroupAdapter.kt b/src/org/thoughtcrime/securesms/loki/activities/EditClosedGroupAdapter.kt deleted file mode 100644 index b41e21a520..0000000000 --- a/src/org/thoughtcrime/securesms/loki/activities/EditClosedGroupAdapter.kt +++ /dev/null @@ -1,48 +0,0 @@ -package org.thoughtcrime.securesms.loki.activities - -import android.R -import android.app.PendingIntent.getActivity -import android.content.Context -import android.support.v7.app.AlertDialog -import android.support.v7.widget.RecyclerView -import android.view.View -import android.view.ViewGroup -import android.widget.AdapterView -import android.widget.EditText -import android.widget.LinearLayout -import org.thoughtcrime.securesms.DeviceListItem -import org.thoughtcrime.securesms.database.Address -import org.thoughtcrime.securesms.loki.dialogs.DeviceEditingOptionsBottomSheet -import org.thoughtcrime.securesms.loki.dialogs.GroupEditingOptionsBottomSheet -import org.thoughtcrime.securesms.loki.utilities.toPx -import org.thoughtcrime.securesms.loki.views.UserView -import org.thoughtcrime.securesms.mms.GlideRequests -import org.thoughtcrime.securesms.recipients.Recipient - -class EditClosedGroupAdapter(private val context: Context) : RecyclerView.Adapter() { - lateinit var glide: GlideRequests - private val selectedMembers = mutableSetOf() - var members = listOf() - set(value) { - field = value; notifyDataSetChanged() - } - var memberClickListener: MemberClickListener? = null - - class ViewHolder(val view: UserView) : RecyclerView.ViewHolder(view) - - override fun getItemCount(): Int { - return members.size - } - - override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { - val view = UserView(context) - return ViewHolder(view) - } - - override fun onBindViewHolder(viewHolder: ViewHolder, position: Int) { - val member = members[position] - viewHolder.view.setOnClickListener { memberClickListener?.onMemberClick(member) } - val isSelected = selectedMembers.contains(member) - viewHolder.view.bind(Recipient.from(context, Address.fromSerialized(member), false), isSelected, glide, true) - } -} diff --git a/src/org/thoughtcrime/securesms/loki/activities/EditClosedGroupMembersAdapter.kt b/src/org/thoughtcrime/securesms/loki/activities/EditClosedGroupMembersAdapter.kt new file mode 100644 index 0000000000..965018f51a --- /dev/null +++ b/src/org/thoughtcrime/securesms/loki/activities/EditClosedGroupMembersAdapter.kt @@ -0,0 +1,42 @@ +package org.thoughtcrime.securesms.loki.activities + +import android.content.Context +import android.support.v7.widget.RecyclerView +import android.view.ViewGroup +import org.thoughtcrime.securesms.database.Address +import org.thoughtcrime.securesms.loki.views.UserView +import org.thoughtcrime.securesms.mms.GlideRequests +import org.thoughtcrime.securesms.recipients.Recipient + +class EditClosedGroupMembersAdapter( + private val context: Context, + private val glide: GlideRequests, + private val memberClickListener: ((String) -> Unit)? = null +) : RecyclerView.Adapter() { + + private val items = ArrayList() + +// private val selectedItems = mutableSetOf() + + fun setItems(items: Collection) { + this.items.clear() + this.items.addAll(items) + notifyDataSetChanged() + } + + override fun getItemCount(): Int = items.size + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { + val view = UserView(context) + return ViewHolder(view) + } + + override fun onBindViewHolder(viewHolder: ViewHolder, position: Int) { + val item = items[position] +// val isSelected = selectedItems.contains(item) + viewHolder.view.bind(Recipient.from(context, Address.fromSerialized(item), false), false, glide, true) + viewHolder.view.setOnClickListener { this.memberClickListener?.invoke(item) } + } + + class ViewHolder(val view: UserView) : RecyclerView.ViewHolder(view) +} \ No newline at end of file diff --git a/src/org/thoughtcrime/securesms/loki/activities/SelectContactsActivity.kt b/src/org/thoughtcrime/securesms/loki/activities/SelectContactsActivity.kt index 9d73deb831..a177a355b8 100644 --- a/src/org/thoughtcrime/securesms/loki/activities/SelectContactsActivity.kt +++ b/src/org/thoughtcrime/securesms/loki/activities/SelectContactsActivity.kt @@ -15,14 +15,15 @@ import network.loki.messenger.R import org.thoughtcrime.securesms.PassphraseRequiredActionBarActivity import org.thoughtcrime.securesms.mms.GlideApp +const val EXTRA_SELECTED_CONTACTS = "SELECTED_CONTACTS_RESULT" + class SelectContactsActivity : PassphraseRequiredActionBarActivity(), MemberClickListener, LoaderManager.LoaderCallbacks> { private var members = listOf() set(value) { field = value; selectContactsAdapter.members = value } private val selectContactsAdapter by lazy { - val result = SelectContactsAdapter(this) - result.glide = GlideApp.with(this) - result.memberClickListener = this + val glide = GlideApp.with(this) + val result = SelectContactsAdapter(this, glide, this) result } @@ -94,7 +95,7 @@ class SelectContactsActivity : PassphraseRequiredActionBarActivity(), MemberClic val selectedMembers = this.selectedMembers val selectedContacts = selectedMembers.toTypedArray() val data = Intent() - data.putExtra("Selected Contacts Result", selectedContacts) + data.putExtra(EXTRA_SELECTED_CONTACTS, selectedContacts) setResult(Activity.RESULT_OK, data) finish() } diff --git a/src/org/thoughtcrime/securesms/loki/activities/SelectContactsAdapter.kt b/src/org/thoughtcrime/securesms/loki/activities/SelectContactsAdapter.kt index e5210099f7..c048bde096 100644 --- a/src/org/thoughtcrime/securesms/loki/activities/SelectContactsAdapter.kt +++ b/src/org/thoughtcrime/securesms/loki/activities/SelectContactsAdapter.kt @@ -8,12 +8,16 @@ import org.thoughtcrime.securesms.loki.views.UserView import org.thoughtcrime.securesms.mms.GlideRequests import org.thoughtcrime.securesms.recipients.Recipient -class SelectContactsAdapter(private val context: Context) : RecyclerView.Adapter() { - lateinit var glide: GlideRequests +class SelectContactsAdapter( + private val context: Context, + private val glide: GlideRequests, + private val memberClickListener: MemberClickListener? = null) + : RecyclerView.Adapter() { + val selectedMembers = mutableSetOf() + var members = listOf() set(value) { field = value; notifyDataSetChanged() } - var memberClickListener: MemberClickListener? = null class ViewHolder(val view: UserView) : RecyclerView.ViewHolder(view) diff --git a/src/org/thoughtcrime/securesms/loki/activities/SettingsActivity.kt b/src/org/thoughtcrime/securesms/loki/activities/SettingsActivity.kt index 79cf26157f..20086dc391 100644 --- a/src/org/thoughtcrime/securesms/loki/activities/SettingsActivity.kt +++ b/src/org/thoughtcrime/securesms/loki/activities/SettingsActivity.kt @@ -77,8 +77,8 @@ class SettingsActivity : PassphraseRequiredActionBarActivity() { profilePictureView.isLarge = true profilePictureView.update() profilePictureView.setOnClickListener { showEditProfilePictureUI() } - displayNameContainer.setOnClickListener { showEditDisplayNameUI() } - displayNameTextView.text = DatabaseFactory.getLokiUserDatabase(this).getDisplayName(hexEncodedPublicKey) + ctnGroupNameSection.setOnClickListener { showEditDisplayNameUI() } + txvGroupNameDisplay.text = DatabaseFactory.getLokiUserDatabase(this).getDisplayName(hexEncodedPublicKey) publicKeyTextView.text = hexEncodedPublicKey copyButton.setOnClickListener { copyPublicKey() } shareButton.setOnClickListener { sharePublicKey() } @@ -132,7 +132,7 @@ class SettingsActivity : PassphraseRequiredActionBarActivity() { cancelButton.visibility = if (isEditingDisplayName) View.VISIBLE else View.GONE showQRCodeButton.visibility = if (isEditingDisplayName) View.GONE else View.VISIBLE saveButton.visibility = if (isEditingDisplayName) View.VISIBLE else View.GONE - displayNameTextView.visibility = if (isEditingDisplayName) View.INVISIBLE else View.VISIBLE + txvGroupNameDisplay.visibility = if (isEditingDisplayName) View.INVISIBLE else View.VISIBLE displayNameEditText.visibility = if (isEditingDisplayName) View.VISIBLE else View.INVISIBLE val titleTextViewLayoutParams = titleTextView.layoutParams as LinearLayout.LayoutParams titleTextViewLayoutParams.leftMargin = if (isEditingDisplayName) toPx(16, resources) else 0 @@ -176,7 +176,7 @@ class SettingsActivity : PassphraseRequiredActionBarActivity() { } all(promises).alwaysUi { if (displayName != null) { - displayNameTextView.text = displayName + txvGroupNameDisplay.text = displayName } displayNameToBeUploaded = null if (isUpdatingProfilePicture && profilePicture != null) { diff --git a/src/org/thoughtcrime/securesms/loki/views/ConversationView.kt b/src/org/thoughtcrime/securesms/loki/views/ConversationView.kt index f859faa85e..ce8092d2ff 100644 --- a/src/org/thoughtcrime/securesms/loki/views/ConversationView.kt +++ b/src/org/thoughtcrime/securesms/loki/views/ConversationView.kt @@ -80,7 +80,7 @@ class ConversationView : LinearLayout { profilePictureView.glide = glide profilePictureView.update() val senderDisplayName = if (thread.recipient.isLocalNumber) context.getString(R.string.note_to_self) else if (!thread.recipient.name.isNullOrEmpty()) thread.recipient.name else thread.recipient.address.toString() - displayNameTextView.text = senderDisplayName + txvGroupNameDisplay.text = senderDisplayName timestampTextView.text = DateUtils.getBriefRelativeTimeSpanString(context, Locale.getDefault(), thread.date) muteIndicatorImageView.visibility = if (thread.recipient.isMuted) VISIBLE else GONE val rawSnippet = thread.getDisplayBody(context) diff --git a/src/org/thoughtcrime/securesms/loki/views/MentionCandidateView.kt b/src/org/thoughtcrime/securesms/loki/views/MentionCandidateView.kt index 6208150b54..392f234d7f 100644 --- a/src/org/thoughtcrime/securesms/loki/views/MentionCandidateView.kt +++ b/src/org/thoughtcrime/securesms/loki/views/MentionCandidateView.kt @@ -30,7 +30,7 @@ class MentionCandidateView(context: Context, attrs: AttributeSet?, defStyleAttr: } private fun update() { - displayNameTextView.text = mentionCandidate.displayName + txvGroupNameDisplay.text = mentionCandidate.displayName profilePictureView.publicKey = mentionCandidate.publicKey profilePictureView.additionalPublicKey = null profilePictureView.isRSSFeed = false