supports change conversation avatar
This commit is contained in:
parent
c4e64c9780
commit
db37f9711a
|
@ -46,6 +46,11 @@ public class ParcelableMediaUpdate implements Parcelable {
|
|||
@Nullable
|
||||
public String alt_text;
|
||||
|
||||
@JsonField(name = "delete_on_success")
|
||||
public boolean delete_on_success;
|
||||
@JsonField(name = "delete_always")
|
||||
public boolean delete_always;
|
||||
|
||||
public ParcelableMediaUpdate() {
|
||||
}
|
||||
|
||||
|
@ -78,9 +83,10 @@ public class ParcelableMediaUpdate implements Parcelable {
|
|||
@Override
|
||||
public String toString() {
|
||||
return "ParcelableMediaUpdate{" +
|
||||
"uri='" + uri + '\'' +
|
||||
"alt_text='" + alt_text + '\'' +
|
||||
", uri='" + uri + '\'' +
|
||||
", type=" + type +
|
||||
", alt_text='" + alt_text + '\'' +
|
||||
", delete_on_success=" + delete_on_success +
|
||||
'}';
|
||||
}
|
||||
|
||||
|
|
|
@ -19,11 +19,14 @@
|
|||
|
||||
package org.mariotaku.twidere.fragment.message
|
||||
|
||||
import android.accounts.AccountManager
|
||||
import android.app.Activity
|
||||
import android.app.Dialog
|
||||
import android.content.ContentValues
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.graphics.Rect
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import android.support.v4.app.DialogFragment
|
||||
import android.support.v4.app.FragmentActivity
|
||||
|
@ -38,30 +41,37 @@ import android.support.v7.widget.RecyclerView
|
|||
import android.support.v7.widget.Toolbar
|
||||
import android.view.*
|
||||
import android.widget.CompoundButton
|
||||
import android.widget.EditText
|
||||
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.*
|
||||
import kotlinx.android.synthetic.main.layout_toolbar_message_conversation_title.*
|
||||
import nl.komponents.kovenant.task
|
||||
import nl.komponents.kovenant.then
|
||||
import nl.komponents.kovenant.ui.alwaysUi
|
||||
import org.mariotaku.abstask.library.TaskStarter
|
||||
import org.mariotaku.chameleon.Chameleon
|
||||
import org.mariotaku.chameleon.ChameleonUtils
|
||||
import org.mariotaku.kpreferences.get
|
||||
import org.mariotaku.ktextension.useCursor
|
||||
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
|
||||
import org.mariotaku.sqliteqb.library.Expression
|
||||
import org.mariotaku.twidere.R
|
||||
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
|
||||
import org.mariotaku.twidere.annotation.AccountType
|
||||
import org.mariotaku.twidere.constant.IntentConstants
|
||||
import org.mariotaku.twidere.constant.IntentConstants.*
|
||||
import org.mariotaku.twidere.constant.nameFirstKey
|
||||
import org.mariotaku.twidere.constant.profileImageStyleKey
|
||||
import org.mariotaku.twidere.extension.applyTheme
|
||||
import org.mariotaku.twidere.extension.getDirectMessageMaxParticipants
|
||||
import org.mariotaku.twidere.extension.model.displayAvatarTo
|
||||
import org.mariotaku.twidere.extension.model.getSubtitle
|
||||
import org.mariotaku.twidere.extension.model.getTitle
|
||||
import org.mariotaku.twidere.extension.model.notificationDisabled
|
||||
import org.mariotaku.twidere.extension.model.*
|
||||
import org.mariotaku.twidere.extension.view.calculateSpaceItemHeight
|
||||
import org.mariotaku.twidere.fragment.BaseDialogFragment
|
||||
import org.mariotaku.twidere.fragment.BaseFragment
|
||||
|
@ -72,7 +82,9 @@ import org.mariotaku.twidere.fragment.message.MessageConversationInfoFragment.Co
|
|||
import org.mariotaku.twidere.model.*
|
||||
import org.mariotaku.twidere.model.ParcelableMessageConversation.ConversationType
|
||||
import org.mariotaku.twidere.model.ParcelableMessageConversation.ExtrasType
|
||||
import org.mariotaku.twidere.model.util.AccountUtils
|
||||
import org.mariotaku.twidere.provider.TwidereDataStore.Messages.Conversations
|
||||
import org.mariotaku.twidere.task.twitter.UpdateStatusTask
|
||||
import org.mariotaku.twidere.task.twitter.message.AddParticipantsTask
|
||||
import org.mariotaku.twidere.task.twitter.message.DestroyConversationTask
|
||||
import org.mariotaku.twidere.task.twitter.message.SetConversationNotificationDisabledTask
|
||||
|
@ -169,6 +181,12 @@ class MessageConversationInfoFragment : BaseFragment(), IToolBarSupportFragment,
|
|||
performAddParticipant(user)
|
||||
}
|
||||
}
|
||||
REQUEST_PICK_MEDIA -> {
|
||||
if (resultCode == Activity.RESULT_OK && data != null) {
|
||||
val uri = MediaPickerActivity.getMediaUris(data).firstOrNull() ?: return
|
||||
performSetConversationAvatar(uri)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -246,7 +264,7 @@ class MessageConversationInfoFragment : BaseFragment(), IToolBarSupportFragment,
|
|||
val task = DestroyConversationTask(context, accountKey, conversationId)
|
||||
task.callback = callback@ { succeed ->
|
||||
val f = weakThis.get() ?: return@callback
|
||||
f.dismissAlertDialogThen("leave_conversation_progress") {
|
||||
f.dismissDialogThen("leave_conversation_progress") {
|
||||
if (succeed) {
|
||||
activity?.setResult(RESULT_CLOSE)
|
||||
activity?.finish()
|
||||
|
@ -262,8 +280,8 @@ class MessageConversationInfoFragment : BaseFragment(), IToolBarSupportFragment,
|
|||
val task = AddParticipantsTask(context, accountKey, conversationId, listOf(user))
|
||||
task.callback = callback@ { succeed ->
|
||||
val f = weakThis.get() ?: return@callback
|
||||
f.dismissAlertDialogThen("add_participant_progress") {
|
||||
loaderManager.restartLoader(0, null, this@MessageConversationInfoFragment)
|
||||
f.dismissDialogThen("add_participant_progress") {
|
||||
loaderManager.restartLoader(0, null, this)
|
||||
}
|
||||
}
|
||||
TaskStarter.execute(task)
|
||||
|
@ -275,8 +293,8 @@ class MessageConversationInfoFragment : BaseFragment(), IToolBarSupportFragment,
|
|||
val task = SetConversationNotificationDisabledTask(context, accountKey, conversationId, disabled)
|
||||
task.callback = callback@ { succeed ->
|
||||
val f = weakThis.get() ?: return@callback
|
||||
f.dismissAlertDialogThen("set_notifications_disabled_progress") {
|
||||
loaderManager.restartLoader(0, null, this@MessageConversationInfoFragment)
|
||||
f.dismissDialogThen("set_notifications_disabled_progress") {
|
||||
loaderManager.restartLoader(0, null, this)
|
||||
}
|
||||
}
|
||||
TaskStarter.execute(task)
|
||||
|
@ -292,20 +310,111 @@ class MessageConversationInfoFragment : BaseFragment(), IToolBarSupportFragment,
|
|||
}
|
||||
}
|
||||
"avatar" -> {
|
||||
|
||||
val intent = ThemedMediaPickerActivity.withThemed(context)
|
||||
.allowMultiple(false)
|
||||
.aspectRatio(1, 1)
|
||||
.containsVideo(false)
|
||||
.build()
|
||||
startActivityForResult(intent, REQUEST_PICK_MEDIA)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private inline fun dismissAlertDialogThen(tag: String, crossinline action: BaseFragment.() -> Unit) {
|
||||
executeAfterFragmentResumed { fragment ->
|
||||
val df = fragment.childFragmentManager.findFragmentByTag(tag) as? DialogFragment
|
||||
df?.dismiss()
|
||||
action(fragment)
|
||||
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)
|
||||
})
|
||||
}
|
||||
|
||||
private fun performSetConversationAvatar(uri: Uri) {
|
||||
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)
|
||||
val media = arrayOf(ParcelableMediaUpdate().apply {
|
||||
this.uri = uri.toString()
|
||||
this.delete_always = true
|
||||
})
|
||||
var deleteAlways: List<UpdateStatusTask.MediaDeletionItem>? = null
|
||||
try {
|
||||
val uploadResult = UpdateStatusTask.uploadAllMediaShared(context,
|
||||
fragment.mediaLoader, upload, account, media, null, true, null)
|
||||
deleteAlways = uploadResult.deleteAlways
|
||||
val avatarId = uploadResult.ids.first()
|
||||
val result = microBlog.updateDmConversationAvatar(conversationId, avatarId).isSuccessful
|
||||
uploadResult.deleteOnSuccess.forEach { it.delete(context) }
|
||||
return@updateAction result
|
||||
} catch (e: UpdateStatusTask.UploadException) {
|
||||
e.deleteAlways?.forEach {
|
||||
it.delete(context)
|
||||
}
|
||||
throw e
|
||||
} finally {
|
||||
deleteAlways?.forEach { it.delete(context) }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
throw UnsupportedOperationException()
|
||||
}, successAction = {
|
||||
putNull(Conversations.CONVERSATION_AVATAR)
|
||||
})
|
||||
}
|
||||
|
||||
private inline fun performUpdateInfo(
|
||||
tag: String,
|
||||
crossinline updateAction: (MessageConversationInfoFragment, AccountDetails, MicroBlog) -> Boolean,
|
||||
crossinline successAction: ContentValues.() -> Unit
|
||||
) {
|
||||
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)
|
||||
if (!updateAction(fragment, account, microBlog)) throw MicroBlogException("Update failed")
|
||||
}.then {
|
||||
val fragment = weakThis.get() ?: throw InterruptedException()
|
||||
val values = ContentValues().apply(successAction)
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class ConversationInfoLoader(
|
||||
private inline fun dismissDialogThen(tag: String, crossinline action: MessageConversationInfoFragment.() -> Unit) {
|
||||
executeAfterFragmentResumed { fragment ->
|
||||
val df = fragment.childFragmentManager.findFragmentByTag(tag) as? DialogFragment
|
||||
df?.dismiss()
|
||||
action(fragment as MessageConversationInfoFragment)
|
||||
}
|
||||
}
|
||||
|
||||
internal class ConversationInfoLoader(
|
||||
context: Context,
|
||||
val accountKey: UserKey,
|
||||
val conversationId: String) : AsyncTaskLoader<ParcelableMessageConversation?>(context) {
|
||||
|
@ -563,7 +672,8 @@ class MessageConversationInfoFragment : BaseFragment(), IToolBarSupportFragment,
|
|||
builder.setView(R.layout.dialog_edit_conversation_name)
|
||||
builder.setNegativeButton(android.R.string.cancel, null)
|
||||
builder.setPositiveButton(android.R.string.ok) { dialog, which ->
|
||||
|
||||
val editName = (dialog as Dialog).findViewById(R.id.editName) as EditText
|
||||
(parentFragment as MessageConversationInfoFragment).performSetConversationName(editName.text.toString())
|
||||
}
|
||||
val dialog = builder.create()
|
||||
dialog.setOnShowListener {
|
||||
|
@ -574,6 +684,7 @@ class MessageConversationInfoFragment : BaseFragment(), IToolBarSupportFragment,
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
class DestroyConversationConfirmDialogFragment : BaseDialogFragment() {
|
||||
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
||||
val builder = AlertDialog.Builder(context)
|
||||
|
@ -626,6 +737,7 @@ class MessageConversationInfoFragment : BaseFragment(), IToolBarSupportFragment,
|
|||
companion object {
|
||||
const val RESULT_CLOSE = 101
|
||||
const val REQUEST_CONVERSATION_ADD_USER = 101
|
||||
const val REQUEST_PICK_MEDIA = 102
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -118,6 +118,9 @@ class UpdateStatusTask(
|
|||
|
||||
// Cleanup
|
||||
pendingUpdate.deleteOnSuccess.forEach { item -> item.delete(context) }
|
||||
} catch (e: UploadException) {
|
||||
e.deleteAlways?.forEach { it.delete(context) }
|
||||
throw e
|
||||
} finally {
|
||||
// Cleanup
|
||||
pendingUpdate.deleteAlways.forEach { item -> item.delete(context) }
|
||||
|
@ -288,10 +291,10 @@ class UpdateStatusTask(
|
|||
}
|
||||
val media = statusUpdate.media.first()
|
||||
try {
|
||||
return getBodyFromMedia(context, mediaLoader, Uri.parse(media.uri), sizeLimit, media.type,
|
||||
false, ContentLengthInputStream.ReadListener { length, position ->
|
||||
stateCallback.onUploadingProgressChanged(-1, position, length)
|
||||
}).use { mediaBody ->
|
||||
return getBodyFromMedia(context, mediaLoader, media, sizeLimit, false,
|
||||
ContentLengthInputStream.ReadListener { length, position ->
|
||||
stateCallback.onUploadingProgressChanged(-1, position, length)
|
||||
}).use { mediaBody ->
|
||||
val photoUpdate = PhotoStatusUpdate(mediaBody.body, pendingUpdate.overrideTexts[updateIndex])
|
||||
return@use microBlog.uploadPhoto(photoUpdate)
|
||||
}
|
||||
|
@ -533,6 +536,8 @@ class UpdateStatusTask(
|
|||
|
||||
class UploadException : UpdateStatusException {
|
||||
|
||||
var deleteAlways: List<MediaDeletionItem>? = null
|
||||
|
||||
constructor() : super()
|
||||
|
||||
constructor(detailMessage: String, throwable: Throwable) : super(detailMessage, throwable)
|
||||
|
@ -643,8 +648,8 @@ class UpdateStatusTask(
|
|||
var body: MediaStreamBody? = null
|
||||
try {
|
||||
val sizeLimit = account.size_limit
|
||||
body = getBodyFromMedia(context, mediaLoader, Uri.parse(media.uri), sizeLimit,
|
||||
media.type, chucked, ContentLengthInputStream.ReadListener { length, position ->
|
||||
body = getBodyFromMedia(context, mediaLoader, media, sizeLimit,
|
||||
chucked, ContentLengthInputStream.ReadListener { length, position ->
|
||||
callback?.onUploadingProgressChanged(index, position, length)
|
||||
})
|
||||
val mediaUploadEvent = MediaUploadEvent.create(context, media)
|
||||
|
@ -660,9 +665,13 @@ class UpdateStatusTask(
|
|||
mediaUploadEvent.markEnd()
|
||||
HotMobiLogger.getInstance(context).log(mediaUploadEvent)
|
||||
} catch (e: IOException) {
|
||||
throw UploadException(e)
|
||||
throw UploadException(e).apply {
|
||||
this.deleteAlways = deleteAlways
|
||||
}
|
||||
} catch (e: MicroBlogException) {
|
||||
throw UploadException(e)
|
||||
throw UploadException(e).apply {
|
||||
this.deleteAlways = deleteAlways
|
||||
}
|
||||
} finally {
|
||||
Utils.closeSilently(body)
|
||||
}
|
||||
|
@ -684,13 +693,14 @@ class UpdateStatusTask(
|
|||
fun getBodyFromMedia(
|
||||
context: Context,
|
||||
mediaLoader: MediaLoaderWrapper,
|
||||
mediaUri: Uri,
|
||||
media: ParcelableMediaUpdate,
|
||||
sizeLimit: SizeLimit? = null,
|
||||
@ParcelableMedia.Type type: Int,
|
||||
chucked: Boolean,
|
||||
readListener: ContentLengthInputStream.ReadListener
|
||||
): MediaStreamBody {
|
||||
val resolver = context.contentResolver
|
||||
val mediaUri = Uri.parse(media.uri)
|
||||
val type = media.type
|
||||
val mediaType = resolver.getType(mediaUri) ?: run {
|
||||
if (mediaUri.scheme == ContentResolver.SCHEME_FILE) {
|
||||
mediaUri.lastPathSegment?.substringAfterLast(".")?.let { ext ->
|
||||
|
@ -716,8 +726,13 @@ class UpdateStatusTask(
|
|||
cis.setReadListener(readListener)
|
||||
val mimeType = data?.type ?: mediaType ?: "application/octet-stream"
|
||||
val body = FileBody(cis, "attachment", cis.length(), ContentType.parse(mimeType))
|
||||
val deleteOnSuccess: MutableList<MediaDeletionItem> = mutableListOf(UriMediaDeletionItem(mediaUri))
|
||||
val deleteOnSuccess: MutableList<MediaDeletionItem> = mutableListOf()
|
||||
val deleteAlways: MutableList<MediaDeletionItem> = mutableListOf()
|
||||
if (media.delete_always) {
|
||||
deleteAlways.add(UriMediaDeletionItem(mediaUri))
|
||||
} else if (media.delete_on_success) {
|
||||
deleteOnSuccess.add(UriMediaDeletionItem(mediaUri))
|
||||
}
|
||||
data?.deleteOnSuccess?.addAllTo(deleteOnSuccess)
|
||||
data?.deleteAlways?.addAllTo(deleteAlways)
|
||||
return MediaStreamBody(body, data?.geometry, deleteOnSuccess, deleteAlways)
|
||||
|
|
|
@ -114,6 +114,11 @@ class SendMessageTask(
|
|||
deleteOnSuccess = uploadResult.deleteOnSuccess
|
||||
}
|
||||
microBlog.sendDm(newDm)
|
||||
} catch (e: UpdateStatusTask.UploadException) {
|
||||
e.deleteAlways?.forEach {
|
||||
it.delete(context)
|
||||
}
|
||||
throw e
|
||||
} finally {
|
||||
deleteOnSuccess?.forEach { it.delete(context) }
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue