Twidere-App-Android-Twitter.../twidere/src/main/kotlin/org/mariotaku/twidere/fragment/message/MessageConversationInfoFrag...

784 lines
33 KiB
Kotlin
Raw Normal View History

2017-02-15 09:11:11 +01:00
/*
* Twidere - Twitter client for Android
*
* Copyright (C) 2012-2017 Mariotaku Lee <mariotaku.lee@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.mariotaku.twidere.fragment.message
2017-02-26 14:49:31 +01:00
import android.accounts.AccountManager
import android.app.Activity
2017-02-22 02:08:22 +01:00
import android.app.Dialog
2017-02-26 14:49:31 +01:00
import android.content.ContentValues
2017-02-20 16:13:48 +01:00
import android.content.Context
import android.content.Intent
2017-02-26 11:59:02 +01:00
import android.graphics.Rect
2017-02-26 14:49:31 +01:00
import android.net.Uri
2017-02-19 15:29:08 +01:00
import android.os.Bundle
2017-02-22 02:08:22 +01:00
import android.support.v4.app.DialogFragment
2017-02-19 15:29:08 +01:00
import android.support.v4.app.FragmentActivity
2017-02-20 16:13:48 +01:00
import android.support.v4.app.LoaderManager
import android.support.v4.content.AsyncTaskLoader
import android.support.v4.content.Loader
2017-02-22 02:08:22 +01:00
import android.support.v7.app.AlertDialog
2017-02-20 16:13:48 +01:00
import android.support.v7.app.AppCompatActivity
import android.support.v7.widget.FixedLinearLayoutManager
import android.support.v7.widget.LinearLayoutManager
import android.support.v7.widget.RecyclerView
2017-02-19 15:29:08 +01:00
import android.support.v7.widget.Toolbar
2017-02-21 16:12:03 +01:00
import android.view.*
import android.widget.CompoundButton
2017-02-26 14:49:31 +01:00
import android.widget.EditText
2017-03-01 15:12:25 +01:00
import com.bumptech.glide.Glide
import com.bumptech.glide.RequestManager
2017-02-19 15:29:08 +01:00
import kotlinx.android.synthetic.main.activity_home_content.view.*
import kotlinx.android.synthetic.main.fragment_messages_conversation_info.*
import kotlinx.android.synthetic.main.header_message_conversation_info.view.*
2017-02-20 16:13:48 +01:00
import kotlinx.android.synthetic.main.layout_toolbar_message_conversation_title.*
2017-02-26 14:49:31 +01:00
import nl.komponents.kovenant.task
import nl.komponents.kovenant.then
import nl.komponents.kovenant.ui.alwaysUi
2017-02-22 02:08:22 +01:00
import org.mariotaku.abstask.library.TaskStarter
2017-02-20 16:13:48 +01:00
import org.mariotaku.chameleon.Chameleon
import org.mariotaku.chameleon.ChameleonUtils
import org.mariotaku.kpreferences.get
2017-02-28 02:41:09 +01:00
import org.mariotaku.ktextension.setItemAvailability
2017-02-20 16:13:48 +01:00
import org.mariotaku.ktextension.useCursor
2017-03-05 09:08:09 +01:00
import org.mariotaku.library.objectcursor.ObjectCursor
2017-02-26 14:49:31 +01:00
import org.mariotaku.microblog.library.MicroBlog
import org.mariotaku.microblog.library.MicroBlogException
import org.mariotaku.microblog.library.twitter.TwitterUpload
import org.mariotaku.pickncrop.library.MediaPickerActivity
2017-02-20 16:13:48 +01:00
import org.mariotaku.sqliteqb.library.Expression
2017-02-19 15:29:08 +01:00
import org.mariotaku.twidere.R
2017-02-26 14:49:31 +01:00
import org.mariotaku.twidere.activity.ThemedMediaPickerActivity
import org.mariotaku.twidere.activity.UserSelectorActivity
import org.mariotaku.twidere.adapter.BaseRecyclerViewAdapter
import org.mariotaku.twidere.adapter.iface.IItemCountsAdapter
2017-02-26 14:49:31 +01:00
import org.mariotaku.twidere.annotation.AccountType
2017-03-06 10:32:58 +01:00
import org.mariotaku.twidere.annotation.ProfileImageSize
import org.mariotaku.twidere.constant.IntentConstants
import org.mariotaku.twidere.constant.IntentConstants.*
2017-02-20 16:13:48 +01:00
import org.mariotaku.twidere.constant.nameFirstKey
import org.mariotaku.twidere.constant.profileImageStyleKey
2017-02-22 02:08:22 +01:00
import org.mariotaku.twidere.extension.applyTheme
2017-02-26 11:59:02 +01:00
import org.mariotaku.twidere.extension.getDirectMessageMaxParticipants
2017-03-02 07:59:19 +01:00
import org.mariotaku.twidere.extension.loadProfileImage
2017-02-26 14:49:31 +01:00
import org.mariotaku.twidere.extension.model.*
2017-02-21 16:12:03 +01:00
import org.mariotaku.twidere.extension.view.calculateSpaceItemHeight
2017-02-22 02:08:22 +01:00
import org.mariotaku.twidere.fragment.BaseDialogFragment
2017-02-15 09:11:11 +01:00
import org.mariotaku.twidere.fragment.BaseFragment
2017-02-22 02:08:22 +01:00
import org.mariotaku.twidere.fragment.ProgressDialogFragment
2017-02-19 15:29:08 +01:00
import org.mariotaku.twidere.fragment.iface.IToolBarSupportFragment
2017-02-26 11:59:02 +01:00
import org.mariotaku.twidere.fragment.message.MessageConversationInfoFragment.ConversationInfoAdapter.Companion.VIEW_TYPE_BOTTOM_SPACE
2017-02-21 16:12:03 +01:00
import org.mariotaku.twidere.fragment.message.MessageConversationInfoFragment.ConversationInfoAdapter.Companion.VIEW_TYPE_HEADER
import org.mariotaku.twidere.model.*
2017-02-25 17:04:57 +01:00
import org.mariotaku.twidere.model.ParcelableMessageConversation.ConversationType
import org.mariotaku.twidere.model.ParcelableMessageConversation.ExtrasType
2017-02-26 14:49:31 +01:00
import org.mariotaku.twidere.model.util.AccountUtils
2017-02-20 16:13:48 +01:00
import org.mariotaku.twidere.provider.TwidereDataStore.Messages.Conversations
2017-02-26 14:49:31 +01:00
import org.mariotaku.twidere.task.twitter.UpdateStatusTask
import org.mariotaku.twidere.task.twitter.message.AddParticipantsTask
2017-02-22 02:08:22 +01:00
import org.mariotaku.twidere.task.twitter.message.DestroyConversationTask
import org.mariotaku.twidere.task.twitter.message.SetConversationNotificationDisabledTask
2017-02-23 02:43:10 +01:00
import org.mariotaku.twidere.util.IntentUtils
import org.mariotaku.twidere.view.holder.SimpleUserViewHolder
2017-02-22 02:08:22 +01:00
import java.lang.ref.WeakReference
2017-02-15 09:11:11 +01:00
/**
* Created by mariotaku on 2017/2/15.
*/
2017-02-20 16:13:48 +01:00
class MessageConversationInfoFragment : BaseFragment(), IToolBarSupportFragment,
LoaderManager.LoaderCallbacks<ParcelableMessageConversation?> {
private val accountKey: UserKey get() = arguments.getParcelable(EXTRA_ACCOUNT_KEY)
private val conversationId: String get() = arguments.getString(EXTRA_CONVERSATION_ID)
private lateinit var adapter: ConversationInfoAdapter
2017-02-26 11:59:02 +01:00
private lateinit var itemDecoration: ConversationInfoDecoration
2017-02-19 15:29:08 +01:00
override val controlBarHeight: Int get() = toolbar.measuredHeight
override var controlBarOffset: Float = 0f
override val toolbar: Toolbar
get() = toolbarLayout.toolbar
2017-02-20 16:13:48 +01:00
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
2017-02-21 16:12:03 +01:00
setHasOptionsMenu(true)
2017-02-20 16:13:48 +01:00
val activity = this.activity
if (activity is AppCompatActivity) {
activity.supportActionBar?.setDisplayShowTitleEnabled(false)
}
2017-02-21 16:12:03 +01:00
val theme = Chameleon.getOverrideTheme(context, activity)
2017-02-20 16:13:48 +01:00
2017-03-02 07:59:19 +01:00
adapter = ConversationInfoAdapter(context, Glide.with(this))
2017-02-23 02:43:10 +01:00
adapter.listener = object : ConversationInfoAdapter.Listener {
override fun onUserClick(position: Int) {
val user = adapter.getUser(position) ?: return
startActivity(IntentUtils.userProfile(user))
}
override fun onAddUserClick(position: Int) {
val conversation = adapter.conversation ?: return
val intent = Intent(IntentConstants.INTENT_ACTION_SELECT_USER)
intent.putExtra(EXTRA_ACCOUNT_KEY, conversation.account_key)
intent.setClass(context, UserSelectorActivity::class.java)
startActivityForResult(intent, REQUEST_CONVERSATION_ADD_USER)
}
override fun onDisableNotificationChanged(disabled: Boolean) {
performSetNotificationDisabled(disabled)
}
2017-02-23 02:43:10 +01:00
}
2017-02-26 11:59:02 +01:00
itemDecoration = ConversationInfoDecoration(adapter,
resources.getDimensionPixelSize(R.dimen.element_spacing_large)
)
recyclerView.adapter = adapter
2017-02-21 16:12:03 +01:00
recyclerView.layoutManager = LayoutManager(context)
2017-02-26 11:59:02 +01:00
recyclerView.addItemDecoration(itemDecoration)
2017-02-20 16:13:48 +01:00
val profileImageStyle = preferences[profileImageStyleKey]
appBarIcon.style = profileImageStyle
2017-02-20 16:13:48 +01:00
conversationAvatar.style = profileImageStyle
val avatarBackground = ChameleonUtils.getColorDependent(theme.colorToolbar)
2017-03-06 10:32:58 +01:00
appBarIcon.setShapeBackground(avatarBackground)
appBarTitle.setTextColor(ChameleonUtils.getColorDependent(theme.colorToolbar))
appBarSubtitle.setTextColor(ChameleonUtils.getColorDependent(theme.colorToolbar))
2017-03-06 10:32:58 +01:00
conversationAvatar.setShapeBackground(avatarBackground)
2017-02-22 02:08:22 +01:00
conversationTitle.setTextColor(ChameleonUtils.getColorDependent(theme.colorToolbar))
conversationSubtitle.setTextColor(ChameleonUtils.getColorDependent(theme.colorToolbar))
2017-02-20 16:13:48 +01:00
editButton.setOnClickListener {
executeAfterFragmentResumed { fragment ->
val df = EditInfoDialogFragment()
df.show(fragment.childFragmentManager, "edit_info")
}
}
loaderManager.initLoader(0, null, this)
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
when (requestCode) {
REQUEST_CONVERSATION_ADD_USER -> {
if (resultCode == Activity.RESULT_OK && data != null) {
val user = data.getParcelableExtra<ParcelableUser>(EXTRA_USER)
performAddParticipant(user)
}
}
2017-02-26 14:49:31 +01:00
REQUEST_PICK_MEDIA -> {
when (resultCode) {
Activity.RESULT_OK -> {
val uri = MediaPickerActivity.getMediaUris(data).firstOrNull() ?: return
performSetConversationAvatar(uri)
}
RESULT_CODE_REMOVE_CONVERSATION_AVATAR -> {
performSetConversationAvatar(null)
}
2017-02-26 14:49:31 +01:00
}
}
}
2017-02-20 16:13:48 +01:00
}
2017-02-19 15:29:08 +01:00
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
return inflater.inflate(R.layout.fragment_messages_conversation_info, container, false)
}
2017-02-21 16:12:03 +01:00
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
inflater.inflate(R.menu.menu_messages_conversation_info, menu)
}
2017-02-28 02:41:09 +01:00
override fun onPrepareOptionsMenu(menu: Menu) {
if (adapter.conversation?.conversation_extras_type == ExtrasType.TWITTER_OFFICIAL) {
menu.setItemAvailability(R.id.leave_conversation, true)
} else {
menu.setItemAvailability(R.id.leave_conversation, false)
}
}
2017-02-22 02:08:22 +01:00
override fun onOptionsItemSelected(item: MenuItem): Boolean {
when (item.itemId) {
R.id.leave_conversation -> {
val df = DestroyConversationConfirmDialogFragment()
df.show(childFragmentManager, "destroy_conversation_confirm")
return true
}
}
return false
}
2017-02-19 15:29:08 +01:00
override fun setupWindow(activity: FragmentActivity): Boolean {
return false
}
2017-02-20 16:13:48 +01:00
override fun onCreateLoader(id: Int, args: Bundle?): Loader<ParcelableMessageConversation?> {
return ConversationInfoLoader(context, accountKey, conversationId)
}
override fun onLoaderReset(loader: Loader<ParcelableMessageConversation?>?) {
}
override fun onLoadFinished(loader: Loader<ParcelableMessageConversation?>?, data: ParcelableMessageConversation?) {
if (data == null) {
activity?.finish()
return
}
2017-02-22 02:08:22 +01:00
val name = data.getTitle(context, userColorNameManager, preferences[nameFirstKey]).first
val summary = data.getSubtitle(context)
2017-03-02 07:59:19 +01:00
val requestManager = Glide.with(this)
val profileImageStyle = preferences[profileImageStyleKey]
requestManager.loadProfileImage(context, data, profileImageStyle).into(conversationAvatar)
requestManager.loadProfileImage(context, data, profileImageStyle, size = ProfileImageSize.REASONABLY_SMALL).into(appBarIcon)
appBarTitle.text = name
2017-02-22 02:08:22 +01:00
conversationTitle.text = name
if (summary != null) {
appBarSubtitle.visibility = View.VISIBLE
conversationSubtitle.visibility = View.VISIBLE
appBarSubtitle.text = summary
conversationSubtitle.text = summary
} else {
appBarSubtitle.visibility = View.GONE
conversationSubtitle.visibility = View.GONE
}
2017-02-26 11:59:02 +01:00
if (data.conversation_extras_type == ExtrasType.TWITTER_OFFICIAL
&& data.conversation_type == ConversationType.GROUP) {
editButton.visibility = View.VISIBLE
adapter.showButtonSpace = true
} else {
editButton.visibility = View.GONE
adapter.showButtonSpace = false
}
adapter.conversation = data
2017-02-22 02:08:22 +01:00
}
private fun performDestroyConversation() {
ProgressDialogFragment.show(childFragmentManager, "leave_conversation_progress")
val weakThis = WeakReference(this)
val task = DestroyConversationTask(context, accountKey, conversationId)
task.callback = callback@ { succeed ->
val f = weakThis.get() ?: return@callback
2017-02-26 14:49:31 +01:00
f.dismissDialogThen("leave_conversation_progress") {
2017-02-22 02:08:22 +01:00
if (succeed) {
activity?.setResult(RESULT_CLOSE)
activity?.finish()
}
}
}
TaskStarter.execute(task)
2017-02-20 16:13:48 +01:00
}
private fun performAddParticipant(user: ParcelableUser) {
ProgressDialogFragment.show(childFragmentManager, "add_participant_progress")
val weakThis = WeakReference(this)
val task = AddParticipantsTask(context, accountKey, conversationId, listOf(user))
task.callback = callback@ { succeed ->
val f = weakThis.get() ?: return@callback
2017-02-26 14:49:31 +01:00
f.dismissDialogThen("add_participant_progress") {
loaderManager.restartLoader(0, null, this)
2017-02-26 09:09:10 +01:00
}
}
TaskStarter.execute(task)
}
private fun performSetNotificationDisabled(disabled: Boolean) {
ProgressDialogFragment.show(childFragmentManager, "set_notifications_disabled_progress")
val weakThis = WeakReference(this)
val task = SetConversationNotificationDisabledTask(context, accountKey, conversationId, disabled)
task.callback = callback@ { succeed ->
val f = weakThis.get() ?: return@callback
2017-02-26 14:49:31 +01:00
f.dismissDialogThen("set_notifications_disabled_progress") {
loaderManager.restartLoader(0, null, this)
}
}
TaskStarter.execute(task)
}
2017-02-26 11:59:02 +01:00
private fun openEditAction(type: String) {
when (type) {
"name" -> {
executeAfterFragmentResumed { fragment ->
val df = EditNameDialogFragment()
df.show(fragment.childFragmentManager, "edit_name")
}
}
"avatar" -> {
2017-02-26 14:49:31 +01:00
val intent = ThemedMediaPickerActivity.withThemed(context)
.allowMultiple(false)
.aspectRatio(1, 1)
.containsVideo(false)
.addEntry(getString(R.string.action_remove_conversation_avatar),
"remove_avatar", RESULT_CODE_REMOVE_CONVERSATION_AVATAR)
2017-02-26 14:49:31 +01:00
.build()
startActivityForResult(intent, REQUEST_PICK_MEDIA)
}
}
}
private fun performSetConversationName(name: String) {
val conversationId = this.conversationId
performUpdateInfo("set_name_progress", updateAction = updateAction@ { fragment, account, microBlog ->
val context = fragment.context
when (account.type) {
AccountType.TWITTER -> {
if (account.isOfficial(context)) {
return@updateAction microBlog.updateDmConversationName(conversationId, name).isSuccessful
}
}
}
throw UnsupportedOperationException()
}, successAction = {
put(Conversations.CONVERSATION_NAME, name)
})
}
2017-02-26 11:59:02 +01:00
private fun performSetConversationAvatar(uri: Uri?) {
2017-02-26 14:49:31 +01:00
val conversationId = this.conversationId
performUpdateInfo("set_avatar_progress", updateAction = updateAction@ { fragment, account, microBlog ->
val context = fragment.context
when (account.type) {
AccountType.TWITTER -> {
if (account.isOfficial(context)) {
val upload = account.newMicroBlogInstance(context, cls = TwitterUpload::class.java)
if (uri == null) {
val result = microBlog.updateDmConversationAvatar(conversationId, null)
if (result.isSuccessful) {
val dmResponse = microBlog.getDmConversation(conversationId, null).conversationTimeline
return@updateAction dmResponse.conversations[conversationId]?.avatarImageHttps
}
throw MicroBlogException("Error ${result.responseCode}")
}
2017-02-26 14:49:31 +01:00
var deleteAlways: List<UpdateStatusTask.MediaDeletionItem>? = null
try {
val media = arrayOf(ParcelableMediaUpdate().apply {
this.uri = uri.toString()
this.delete_always = true
})
2017-02-26 14:49:31 +01:00
val uploadResult = UpdateStatusTask.uploadAllMediaShared(context,
upload, account, media, null, true, null)
2017-02-26 14:49:31 +01:00
deleteAlways = uploadResult.deleteAlways
val avatarId = uploadResult.ids.first()
val result = microBlog.updateDmConversationAvatar(conversationId, avatarId)
if (result.isSuccessful) {
uploadResult.deleteOnSuccess.forEach { it.delete(context) }
val dmResponse = microBlog.getDmConversation(conversationId, null).conversationTimeline
return@updateAction dmResponse.conversations[conversationId]?.avatarImageHttps
}
throw MicroBlogException("Error ${result.responseCode}")
2017-02-26 14:49:31 +01:00
} catch (e: UpdateStatusTask.UploadException) {
e.deleteAlways?.forEach {
it.delete(context)
}
throw e
} finally {
deleteAlways?.forEach { it.delete(context) }
}
}
}
}
throw UnsupportedOperationException()
}, successAction = { uri ->
put(Conversations.CONVERSATION_AVATAR, uri)
2017-02-26 14:49:31 +01:00
})
}
private inline fun <T> performUpdateInfo(
2017-02-26 14:49:31 +01:00
tag: String,
crossinline updateAction: (MessageConversationInfoFragment, AccountDetails, MicroBlog) -> T,
crossinline successAction: ContentValues.(T) -> Unit
2017-02-26 14:49:31 +01:00
) {
ProgressDialogFragment.show(childFragmentManager, tag)
val weakThis = WeakReference(this)
val accountKey = this.accountKey
val conversationId = this.conversationId
task {
val fragment = weakThis.get() ?: throw InterruptedException()
val account = AccountUtils.getAccountDetails(AccountManager.get(fragment.context),
accountKey, true) ?: throw MicroBlogException("No account")
val microBlog = account.newMicroBlogInstance(fragment.context, cls = MicroBlog::class.java)
return@task updateAction(fragment, account, microBlog)
}.then { result ->
2017-02-26 14:49:31 +01:00
val fragment = weakThis.get() ?: throw InterruptedException()
val values = ContentValues().apply { successAction(result) }
2017-02-26 14:49:31 +01:00
val where = Expression.and(Expression.equalsArgs(Conversations.ACCOUNT_KEY),
Expression.equalsArgs(Conversations.CONVERSATION_ID)).sql
val whereArgs = arrayOf(accountKey.toString(), conversationId)
fragment.context.contentResolver.update(Conversations.CONTENT_URI, values, where,
whereArgs)
}.alwaysUi {
val fragment = weakThis.get() ?: return@alwaysUi
fragment.dismissDialogThen(tag) {
loaderManager.restartLoader(0, null, this)
2017-02-26 11:59:02 +01:00
}
}
}
2017-02-26 14:49:31 +01:00
private inline fun dismissDialogThen(tag: String, crossinline action: MessageConversationInfoFragment.() -> Unit) {
executeAfterFragmentResumed { fragment ->
val df = fragment.childFragmentManager.findFragmentByTag(tag) as? DialogFragment
df?.dismiss()
2017-02-26 14:49:31 +01:00
action(fragment as MessageConversationInfoFragment)
}
}
2017-02-26 14:49:31 +01:00
internal class ConversationInfoLoader(
2017-02-20 16:13:48 +01:00
context: Context,
val accountKey: UserKey,
val conversationId: String) : AsyncTaskLoader<ParcelableMessageConversation?>(context) {
2017-02-26 11:59:02 +01:00
2017-02-20 16:13:48 +01:00
override fun loadInBackground(): ParcelableMessageConversation? {
val where = Expression.and(Expression.equalsArgs(Conversations.ACCOUNT_KEY),
Expression.equalsArgs(Conversations.CONVERSATION_ID)).sql
val whereArgs = arrayOf(accountKey.toString(), conversationId)
context.contentResolver.query(Conversations.CONTENT_URI, Conversations.COLUMNS, where,
whereArgs, null).useCursor { cur ->
if (cur.moveToFirst()) {
2017-03-05 09:08:09 +01:00
val indices = ObjectCursor.indicesFrom(cur, ParcelableMessageConversation::class.java)
return indices.newObject(cur)
2017-02-20 16:13:48 +01:00
}
}
return null
}
override fun onStartLoading() {
forceLoad()
}
2017-02-26 11:59:02 +01:00
2017-02-20 16:13:48 +01:00
}
2017-03-01 15:12:25 +01:00
class ConversationInfoAdapter(
context: Context,
2017-03-02 07:59:19 +01:00
requestManager: RequestManager
) : BaseRecyclerViewAdapter<RecyclerView.ViewHolder>(context, requestManager),
IItemCountsAdapter {
private val inflater = LayoutInflater.from(context)
2017-02-26 11:59:02 +01:00
override val itemCounts: ItemCounts = ItemCounts(5)
2017-02-22 02:39:15 +01:00
var listener: Listener? = null
var conversation: ParcelableMessageConversation? = null
set(value) {
field = value
notifyDataSetChanged()
}
2017-02-26 11:59:02 +01:00
var showButtonSpace: Boolean = false
set(value) {
field = value
notifyDataSetChanged()
}
2017-02-23 02:43:10 +01:00
init {
setHasStableIds(true)
}
override fun getItemCount(): Int {
val conversation = this.conversation ?: return 0
2017-02-25 17:04:57 +01:00
val participantsSize = conversation.participants.size
2017-02-26 11:59:02 +01:00
itemCounts[ITEM_INDEX_TOP_SPACE] = if (showButtonSpace) 1 else 0
itemCounts[ITEM_INDEX_HEADER] = 1
2017-02-25 17:04:57 +01:00
itemCounts[ITEM_INDEX_ITEM] = participantsSize
when (conversation.conversation_type) {
ConversationType.GROUP -> {
if (participantsSize < defaultFeatures.getDirectMessageMaxParticipants(conversation.conversation_extras_type)) {
itemCounts[ITEM_INDEX_ADD_USER] = 1
} else {
itemCounts[ITEM_INDEX_ADD_USER] = 0
}
}
else -> {
itemCounts[ITEM_INDEX_ADD_USER] = 0
}
}
2017-02-21 16:12:03 +01:00
itemCounts[ITEM_INDEX_SPACE] = 1
return itemCounts.itemCount
}
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
when (holder.itemViewType) {
VIEW_TYPE_HEADER -> {
(holder as HeaderViewHolder).display(this.conversation!!)
}
VIEW_TYPE_USER -> {
2017-02-21 16:12:03 +01:00
val participantIdx = position - itemCounts.getItemStartPosition(ITEM_INDEX_ITEM)
2017-02-23 02:43:10 +01:00
val user = getUser(position)!!
2017-02-21 16:12:03 +01:00
(holder as UserViewHolder).display(user, participantIdx == 0)
}
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
when (viewType) {
2017-02-26 11:59:02 +01:00
VIEW_TYPE_TOP_SPACE -> {
val view = inflater.inflate(R.layout.header_message_conversation_info_button_space, parent, false)
return SpaceViewHolder(view)
}
VIEW_TYPE_HEADER -> {
val view = inflater.inflate(HeaderViewHolder.layoutResource, parent, false)
return HeaderViewHolder(view, this)
}
VIEW_TYPE_USER -> {
2017-02-21 16:12:03 +01:00
val view = inflater.inflate(R.layout.list_item_conversation_info_user, parent, false)
return UserViewHolder(view, this)
}
VIEW_TYPE_ADD_USER -> {
val view = inflater.inflate(R.layout.list_item_conversation_info_add_user, parent, false)
2017-02-22 02:39:15 +01:00
return AddUserViewHolder(view, this)
2017-02-21 16:12:03 +01:00
}
2017-02-26 11:59:02 +01:00
VIEW_TYPE_BOTTOM_SPACE -> {
2017-02-21 16:12:03 +01:00
val view = inflater.inflate(R.layout.list_item_conversation_info_space, parent, false)
return SpaceViewHolder(view)
}
}
throw UnsupportedOperationException()
}
override fun getItemViewType(position: Int): Int {
when (itemCounts.getItemCountIndex(position)) {
2017-02-26 11:59:02 +01:00
ITEM_INDEX_TOP_SPACE -> return VIEW_TYPE_TOP_SPACE
ITEM_INDEX_HEADER -> return VIEW_TYPE_HEADER
ITEM_INDEX_ITEM -> return VIEW_TYPE_USER
2017-02-21 16:12:03 +01:00
ITEM_INDEX_ADD_USER -> return VIEW_TYPE_ADD_USER
2017-02-26 11:59:02 +01:00
ITEM_INDEX_SPACE -> return VIEW_TYPE_BOTTOM_SPACE
}
throw UnsupportedOperationException()
}
2017-02-23 02:43:10 +01:00
override fun getItemId(position: Int): Long {
when (itemCounts.getItemCountIndex(position)) {
ITEM_INDEX_ITEM -> {
val user = getUser(position)!!
return user.hashCode().toLong()
}
else -> {
return Integer.MAX_VALUE.toLong() + getItemViewType(position)
}
}
}
fun getUser(position: Int): ParcelableUser? {
val itemPos = position - itemCounts.getItemStartPosition(ITEM_INDEX_ITEM)
return conversation?.participants?.getOrNull(itemPos)
}
2017-02-22 02:39:15 +01:00
interface Listener {
2017-02-23 02:43:10 +01:00
fun onUserClick(position: Int) {}
fun onAddUserClick(position: Int) {}
fun onDisableNotificationChanged(disabled: Boolean) {}
2017-02-26 11:59:02 +01:00
2017-02-22 02:39:15 +01:00
}
companion object {
2017-02-26 11:59:02 +01:00
internal const val ITEM_INDEX_TOP_SPACE = 0
internal const val ITEM_INDEX_HEADER = 1
internal const val ITEM_INDEX_ITEM = 2
internal const val ITEM_INDEX_ADD_USER = 3
internal const val ITEM_INDEX_SPACE = 4
2017-02-26 11:59:02 +01:00
internal const val VIEW_TYPE_TOP_SPACE = 0
2017-02-21 16:12:03 +01:00
internal const val VIEW_TYPE_HEADER = 1
internal const val VIEW_TYPE_USER = 2
internal const val VIEW_TYPE_ADD_USER = 3
2017-02-26 11:59:02 +01:00
internal const val VIEW_TYPE_BOTTOM_SPACE = 4
2017-02-26 11:59:02 +01:00
}
2017-02-22 02:39:15 +01:00
}
2017-02-21 16:12:03 +01:00
internal class SpaceViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView)
2017-02-22 02:39:15 +01:00
internal class AddUserViewHolder(itemView: View, adapter: ConversationInfoAdapter) : RecyclerView.ViewHolder(itemView) {
2017-02-26 11:59:02 +01:00
2017-02-22 02:39:15 +01:00
private val itemContent = itemView.findViewById(R.id.itemContent)
init {
itemContent.setOnClickListener {
2017-02-23 02:43:10 +01:00
adapter.listener?.onAddUserClick(layoutPosition)
2017-02-22 02:39:15 +01:00
}
}
}
2017-02-21 16:12:03 +01:00
2017-02-22 02:39:15 +01:00
internal class UserViewHolder(
itemView: View,
adapter: ConversationInfoAdapter
) : SimpleUserViewHolder<ConversationInfoAdapter>(itemView, adapter) {
2017-02-21 16:12:03 +01:00
private val headerIcon = itemView.findViewById(R.id.headerIcon)
2017-02-26 11:59:02 +01:00
2017-02-22 02:39:15 +01:00
private val itemContent = itemView.findViewById(R.id.itemContent)
init {
itemContent.setOnClickListener {
adapter.listener?.onUserClick(layoutPosition)
}
}
2017-02-21 16:12:03 +01:00
fun display(user: ParcelableUser, displayHeaderIcon: Boolean) {
super.displayUser(user)
headerIcon.visibility = if (displayHeaderIcon) View.VISIBLE else View.INVISIBLE
}
2017-02-26 11:59:02 +01:00
2017-02-21 16:12:03 +01:00
}
internal class HeaderViewHolder(itemView: View, adapter: ConversationInfoAdapter) : RecyclerView.ViewHolder(itemView) {
2017-02-26 11:59:02 +01:00
private val muteSwitch = itemView.muteNotifications
private val listener = CompoundButton.OnCheckedChangeListener { button, checked ->
adapter.listener?.onDisableNotificationChanged(checked)
}
fun display(conversation: ParcelableMessageConversation) {
muteSwitch.setOnCheckedChangeListener(null)
muteSwitch.isChecked = conversation.notificationDisabled
muteSwitch.setOnCheckedChangeListener(listener)
}
companion object {
const val layoutResource = R.layout.header_message_conversation_info
}
2017-02-26 11:59:02 +01:00
}
2017-02-21 16:12:03 +01:00
internal class LayoutManager(
context: Context
) : FixedLinearLayoutManager(context, LinearLayoutManager.VERTICAL, false) {
override fun getDecoratedMeasuredHeight(child: View): Int {
2017-02-26 11:59:02 +01:00
if (getItemViewType(child) == VIEW_TYPE_BOTTOM_SPACE) {
2017-03-02 02:24:09 +01:00
val height = calculateSpaceItemHeight(child, VIEW_TYPE_BOTTOM_SPACE, VIEW_TYPE_HEADER)
if (height >= 0) {
return height
}
2017-02-21 16:12:03 +01:00
}
return super.getDecoratedMeasuredHeight(child)
}
}
2017-02-22 02:08:22 +01:00
class EditInfoDialogFragment : BaseDialogFragment() {
2017-02-26 11:59:02 +01:00
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
val actions = arrayOf(Action(getString(R.string.action_edit_conversation_name), "name"),
Action(getString(R.string.action_edit_conversation_avatar), "avatar"))
val builder = AlertDialog.Builder(context)
builder.setItems(actions.map(Action::title).toTypedArray()) { dialog, which ->
2017-02-26 11:59:02 +01:00
val action = actions[which]
(parentFragment as MessageConversationInfoFragment).openEditAction(action.type)
}
val dialog = builder.create()
dialog.setOnShowListener {
it as AlertDialog
it.applyTheme()
}
return dialog
}
data class Action(val title: String, val type: String)
2017-02-26 11:59:02 +01:00
}
class EditNameDialogFragment : BaseDialogFragment() {
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
val builder = AlertDialog.Builder(context)
builder.setView(R.layout.dialog_edit_conversation_name)
builder.setNegativeButton(android.R.string.cancel, null)
builder.setPositiveButton(android.R.string.ok) { dialog, which ->
2017-02-26 14:49:31 +01:00
val editName = (dialog as Dialog).findViewById(R.id.editName) as EditText
(parentFragment as MessageConversationInfoFragment).performSetConversationName(editName.text.toString())
2017-02-26 11:59:02 +01:00
}
val dialog = builder.create()
dialog.setOnShowListener {
it as AlertDialog
it.applyTheme()
}
return dialog
}
}
2017-02-26 14:49:31 +01:00
2017-02-22 02:08:22 +01:00
class DestroyConversationConfirmDialogFragment : BaseDialogFragment() {
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
val builder = AlertDialog.Builder(context)
builder.setMessage(R.string.message_destroy_conversation_confirm)
builder.setPositiveButton(R.string.action_leave_conversation) { dialog, which ->
(parentFragment as MessageConversationInfoFragment).performDestroyConversation()
}
builder.setNegativeButton(android.R.string.cancel, null)
val dialog = builder.create()
dialog.setOnShowListener {
it as AlertDialog
it.applyTheme()
}
return dialog
}
}
2017-02-26 11:59:02 +01:00
internal class ConversationInfoDecoration(
val adapter: ConversationInfoAdapter,
val typeSpacing: Int
) : RecyclerView.ItemDecoration() {
override fun getItemOffsets(outRect: Rect, view: View, parent: RecyclerView, state: RecyclerView.State) {
val position = parent.getChildLayoutPosition(view)
if (position < 0) return
val itemCounts = adapter.itemCounts
val countIndex = itemCounts.getItemCountIndex(position)
when (countIndex) {
ConversationInfoAdapter.ITEM_INDEX_TOP_SPACE,
ConversationInfoAdapter.ITEM_INDEX_SPACE,
ConversationInfoAdapter.ITEM_INDEX_ADD_USER -> {
outRect.setEmpty()
}
else -> {
// Previous item is space or first item
if (position == 0 || itemCounts.getItemCountIndex(position - 1)
== ConversationInfoAdapter.ITEM_INDEX_TOP_SPACE) {
outRect.setEmpty()
} else if (itemCounts.getItemStartPosition(countIndex) == position) {
outRect.set(0, typeSpacing, 0, 0)
} else {
outRect.setEmpty()
}
}
}
}
}
2017-02-22 02:08:22 +01:00
companion object {
const val RESULT_CLOSE = 101
const val REQUEST_CONVERSATION_ADD_USER = 101
2017-02-26 14:49:31 +01:00
const val REQUEST_PICK_MEDIA = 102
const val RESULT_CODE_REMOVE_CONVERSATION_AVATAR = 10
2017-02-22 02:08:22 +01:00
}
2017-02-25 17:04:57 +01:00
}