added send dm media, close #831

This commit is contained in:
Mariotaku Lee 2017-05-11 13:01:01 +08:00
parent c62231da97
commit cc91209578
No known key found for this signature in database
GPG Key ID: 15C10F89D7C33535
17 changed files with 453 additions and 59 deletions

View File

@ -18,6 +18,7 @@
package org.mariotaku.microblog.library.twitter;
import org.mariotaku.microblog.library.twitter.api.DirectMessagesEventResources;
import org.mariotaku.microblog.library.twitter.api.DirectMessagesResources;
import org.mariotaku.microblog.library.twitter.api.FavoritesResources;
import org.mariotaku.microblog.library.twitter.api.FriendsFollowersResources;
@ -37,7 +38,8 @@ import org.mariotaku.microblog.library.twitter.api.UsersResources;
* Created by mariotaku on 16/5/13.
*/
public interface Twitter extends SearchResources, TimelineResources, TweetResources, UsersResources,
ListResources, DirectMessagesResources, FriendsFollowersResources, FavoritesResources,
SpamReportingResources, SavedSearchesResources, TrendsResources, PlacesGeoResources,
ListResources, DirectMessagesResources, DirectMessagesEventResources,
FriendsFollowersResources, FavoritesResources, SpamReportingResources,
SavedSearchesResources, TrendsResources, PlacesGeoResources,
HelpResources, MutesResources, TwitterPrivate {
}

View File

@ -46,6 +46,7 @@ public interface TwitterUpload {
@Params(@KeyValue(key = "command", value = "INIT"))
MediaUploadResponse initUploadMedia(@Param("media_type") String mediaType,
@Param("total_bytes") long totalBytes,
@Param("media_category") String mediaCategory,
@Param(value = "additional_owners", arrayDelimiter = ',')
String[] additionalOwners) throws MicroBlogException;

View File

@ -0,0 +1,40 @@
/*
* Twidere - Twitter client for Android
*
* Copyright 2012-2017 Mariotaku Lee <mariotaku.lee@gmail.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.mariotaku.microblog.library.twitter.api;
import org.mariotaku.microblog.library.MicroBlogException;
import org.mariotaku.microblog.library.twitter.model.DirectMessageEventObject;
import org.mariotaku.microblog.library.twitter.template.DirectMessageAnnotationTemplate;
import org.mariotaku.restfu.annotation.method.POST;
import org.mariotaku.restfu.annotation.param.Params;
import org.mariotaku.restfu.annotation.param.Raw;
import org.mariotaku.restfu.http.BodyType;
/**
* Created by mariotaku on 2017/5/11.
*/
@Params(template = DirectMessageAnnotationTemplate.class)
public interface DirectMessagesEventResources {
@POST("/direct_messages/events/new.json")
@BodyType(BodyType.RAW)
DirectMessageEventObject newDirectMessageEvent(@Raw(contentType = "application/json", encoding = "UTF-8")
DirectMessageEventObject event) throws MicroBlogException;
}

View File

@ -22,19 +22,16 @@ import org.mariotaku.microblog.library.MicroBlogException;
import org.mariotaku.microblog.library.twitter.model.DirectMessage;
import org.mariotaku.microblog.library.twitter.model.Paging;
import org.mariotaku.microblog.library.twitter.model.ResponseList;
import org.mariotaku.microblog.library.twitter.template.DirectMessageAnnotationTemplate;
import org.mariotaku.restfu.annotation.method.GET;
import org.mariotaku.restfu.annotation.method.POST;
import org.mariotaku.restfu.annotation.param.KeyValue;
import org.mariotaku.restfu.annotation.param.Param;
import org.mariotaku.restfu.annotation.param.Params;
import org.mariotaku.restfu.annotation.param.Query;
import org.mariotaku.restfu.http.BodyType;
@SuppressWarnings("RedundantThrows")
@Params({@KeyValue(key = "full_text", valueKey = "full_text"),
@KeyValue(key = "include_entities", valueKey = "include_entities"),
@KeyValue(key = "include_cards", valueKey = "include_cards"),
@KeyValue(key = "cards_platform", valueKey = "cards_platform")})
@Params(template = DirectMessageAnnotationTemplate.class)
public interface DirectMessagesResources {
@POST("/direct_messages/destroy.json")

View File

@ -0,0 +1,177 @@
/*
* Twidere - Twitter client for Android
*
* Copyright 2012-2017 Mariotaku Lee <mariotaku.lee@gmail.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.mariotaku.microblog.library.twitter.model;
import com.bluelinelabs.logansquare.annotation.JsonField;
import com.bluelinelabs.logansquare.annotation.JsonObject;
/**
* Created by mariotaku on 2017/5/11.
*/
@JsonObject
public class DirectMessageEventObject {
@JsonField(name = "event")
Event event;
public Event getEvent() {
return event;
}
public void setEvent(Event event) {
this.event = event;
}
@JsonObject
public static class Event {
@JsonField(name = "id")
String id;
@JsonField(name = "type")
String type;
@JsonField(name = "message_create")
MessageCreate messageCreate;
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public MessageCreate getMessageCreate() {
return messageCreate;
}
public void setMessageCreate(MessageCreate messageCreate) {
this.messageCreate = messageCreate;
}
public String getId() {
return id;
}
@JsonObject
public static class MessageCreate {
@JsonField(name = "target")
Target target;
@JsonField(name = "message_data")
MessageData messageData;
public Target getTarget() {
return target;
}
public void setTarget(Target target) {
this.target = target;
}
public MessageData getMessageData() {
return messageData;
}
public void setMessageData(MessageData messageData) {
this.messageData = messageData;
}
@JsonObject
public static class Target {
@JsonField(name = "recipient_id")
String recipientId;
public String getRecipientId() {
return recipientId;
}
public void setRecipientId(String recipientId) {
this.recipientId = recipientId;
}
}
@JsonObject
public static class MessageData {
@JsonField(name = "text")
String text;
@JsonField(name = "attachment")
Attachment attachment;
public String getText() {
return text;
}
public void setText(String text) {
this.text = text;
}
public Attachment getAttachment() {
return attachment;
}
public void setAttachment(Attachment attachment) {
this.attachment = attachment;
}
@JsonObject
public static class Attachment {
@JsonField(name = "type")
String type;
@JsonField(name = "media")
Media media;
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public Media getMedia() {
return media;
}
public void setMedia(Media media) {
this.media = media;
}
@JsonObject
public static class Media {
@JsonField(name = "id")
String id;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
}
}
}
}
}
}

View File

@ -0,0 +1,33 @@
/*
* Twidere - Twitter client for Android
*
* Copyright 2012-2017 Mariotaku Lee <mariotaku.lee@gmail.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.mariotaku.microblog.library.twitter.template;
import org.mariotaku.restfu.annotation.param.KeyValue;
import org.mariotaku.restfu.annotation.param.Params;
/**
* Created by mariotaku on 2017/5/11.
*/
@Params({@KeyValue(key = "full_text", valueKey = "full_text"),
@KeyValue(key = "include_entities", valueKey = "include_entities"),
@KeyValue(key = "include_cards", valueKey = "include_cards"),
@KeyValue(key = "cards_platform", valueKey = "cards_platform")})
public class DirectMessageAnnotationTemplate {
}

View File

@ -28,6 +28,10 @@ operator fun Bundle.set(key: String, value: String?) {
return putString(key, value)
}
operator fun Bundle.set(key: String, value: IntArray?) {
return putIntArray(key, value)
}
operator fun Bundle.set(key: String, value: Parcelable?) {
return putParcelable(key, value)
}

View File

@ -395,7 +395,7 @@ class ComposeActivity : BaseActivity(), OnMenuItemClickListener, OnClickListener
REQUEST_TAKE_PHOTO, REQUEST_PICK_MEDIA -> {
if (resultCode == Activity.RESULT_OK && data != null) {
val src = MediaPickerActivity.getMediaUris(data)
TaskStarter.execute(AddMediaTask(this, src, false, false))
TaskStarter.execute(AddMediaTask(this, src, null, false, false))
val extras = data.getBundleExtra(MediaPickerActivity.EXTRA_EXTRAS)
if (extras?.getBoolean(EXTRA_IS_POSSIBLY_SENSITIVE) ?: false) {
possiblySensitive = true
@ -432,7 +432,7 @@ class ComposeActivity : BaseActivity(), OnMenuItemClickListener, OnClickListener
val src = MediaPickerActivity.getMediaUris(data)?.takeIf(Array<Uri>::isNotEmpty) ?:
data.getParcelableExtra<Uri>(EXTRA_IMAGE_URI)?.let { arrayOf(it) }
if (src != null) {
TaskStarter.execute(AddMediaTask(this, src, false, false))
TaskStarter.execute(AddMediaTask(this, src, null, false, false))
}
}
}
@ -1097,7 +1097,7 @@ class ComposeActivity : BaseActivity(), OnMenuItemClickListener, OnClickListener
val stream = intent.getParcelableExtra<Uri>(Intent.EXTRA_STREAM)
if (stream != null) {
val src = arrayOf(stream)
TaskStarter.execute(AddMediaTask(this, src, true, false))
TaskStarter.execute(AddMediaTask(this, src, null, true, false))
}
} else if (Intent.ACTION_SEND_MULTIPLE == action) {
shouldSaveAccounts = false
@ -1105,7 +1105,7 @@ class ComposeActivity : BaseActivity(), OnMenuItemClickListener, OnClickListener
val extraStream = intent.getParcelableArrayListExtra<Uri>(Intent.EXTRA_STREAM)
if (extraStream != null) {
val src = extraStream.toTypedArray()
TaskStarter.execute(AddMediaTask(this, src, true, false))
TaskStarter.execute(AddMediaTask(this, src, null, true, false))
}
} else {
shouldSaveAccounts = !hasAccountKeys
@ -1113,7 +1113,7 @@ class ComposeActivity : BaseActivity(), OnMenuItemClickListener, OnClickListener
val data = intent.data
if (data != null) {
val src = arrayOf(data)
TaskStarter.execute(AddMediaTask(this, src, true, false))
TaskStarter.execute(AddMediaTask(this, src, null, true, false))
}
}
val extraSubject = intent.getCharSequenceExtra(Intent.EXTRA_SUBJECT)
@ -1788,7 +1788,7 @@ class ComposeActivity : BaseActivity(), OnMenuItemClickListener, OnClickListener
})
editText.customSelectionActionModeCallback = this
editText.imageInputListener = { contentInfo ->
val task = AddMediaTask(this, arrayOf(contentInfo.contentUri), true, false)
val task = AddMediaTask(this, arrayOf(contentInfo.contentUri), null, true, false)
TaskStarter.execute(task)
task.callback = {
contentInfo.releasePermission()
@ -2032,9 +2032,9 @@ class ComposeActivity : BaseActivity(), OnMenuItemClickListener, OnClickListener
}
}
private class AddMediaTask(activity: ComposeActivity, sources: Array<Uri>, copySrc: Boolean,
deleteSrc: Boolean) : AbsAddMediaTask<((List<ParcelableMediaUpdate>?) -> Unit)?>(
activity, sources, copySrc, deleteSrc) {
private class AddMediaTask(activity: ComposeActivity, sources: Array<Uri>, types: IntArray?,
copySrc: Boolean, deleteSrc: Boolean) : AbsAddMediaTask<((List<ParcelableMediaUpdate>?) -> Unit)?>(
activity, sources, types, copySrc, deleteSrc) {
override fun afterExecute(callback: ((List<ParcelableMediaUpdate>?) -> Unit)?,
result: List<ParcelableMediaUpdate>?) {

View File

@ -13,6 +13,9 @@ import org.mariotaku.twidere.util.BitmapFactoryUtils
fun ParcelableMediaUpdate.getMimeType(resolver: ContentResolver): String? {
val uri = Uri.parse(this.uri)
return resolver.getType(uri) ?: when (type) {
ParcelableMedia.Type.ANIMATED_GIF -> {
return "image/gif"
}
ParcelableMedia.Type.IMAGE -> {
val o = BitmapFactory.Options()
o.inJustDecodeBounds = true

View File

@ -0,0 +1,63 @@
/*
* 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.extension.model.api
import org.mariotaku.microblog.library.twitter.model.DirectMessageEventObject
/**
* Created by mariotaku on 2017/5/11.
*/
fun DirectMessageEventObject(action: DirectMessageEventObject.Event.() -> Unit): DirectMessageEventObject {
val obj = DirectMessageEventObject()
val event = DirectMessageEventObject.Event()
action(event)
obj.event = event
return obj
}
fun DirectMessageEventObject.Event.messageCreate(action: DirectMessageEventObject.Event.MessageCreate.() -> Unit) {
val messageCreate = DirectMessageEventObject.Event.MessageCreate()
action(messageCreate)
this.messageCreate = messageCreate
}
fun DirectMessageEventObject.Event.MessageCreate.target(action: DirectMessageEventObject.Event.MessageCreate.Target.() -> Unit) {
val target = DirectMessageEventObject.Event.MessageCreate.Target()
action(target)
this.target = target
}
fun DirectMessageEventObject.Event.MessageCreate.messageData(action: DirectMessageEventObject.Event.MessageCreate.MessageData.() -> Unit) {
val messageData = DirectMessageEventObject.Event.MessageCreate.MessageData()
action(messageData)
this.messageData = messageData
}
fun DirectMessageEventObject.Event.MessageCreate.MessageData.attachment(action: DirectMessageEventObject.Event.MessageCreate.MessageData.Attachment.() -> Unit) {
val attachment = DirectMessageEventObject.Event.MessageCreate.MessageData.Attachment()
action(attachment)
this.attachment = attachment
}
fun DirectMessageEventObject.Event.MessageCreate.MessageData.Attachment.media(action: DirectMessageEventObject.Event.MessageCreate.MessageData.Attachment.Media.() -> Unit) {
val media = DirectMessageEventObject.Event.MessageCreate.MessageData.Attachment.Media()
action(media)
this.media = media
}

View File

@ -33,6 +33,7 @@ import org.mariotaku.twidere.fragment.iface.IBaseFragment
import org.mariotaku.twidere.model.DefaultFeatures
import org.mariotaku.twidere.util.*
import org.mariotaku.twidere.util.dagger.GeneralComponent
import org.mariotaku.twidere.util.gifshare.GifShareProvider
import org.mariotaku.twidere.util.premium.ExtraFeaturesService
import org.mariotaku.twidere.util.schedule.StatusScheduleProvider
import org.mariotaku.twidere.util.sync.SyncPreferences
@ -73,6 +74,8 @@ open class BaseFragment : Fragment(), IBaseFragment<BaseFragment> {
@Inject
lateinit var timelineSyncManagerFactory: TimelineSyncManager.Factory
@Inject
lateinit var gifShareProviderFactory: GifShareProvider.Factory
@Inject
lateinit var restHttpClient: RestHttpClient
@Inject
lateinit var dns: Dns
@ -85,6 +88,9 @@ open class BaseFragment : Fragment(), IBaseFragment<BaseFragment> {
protected val timelineSyncManager: TimelineSyncManager?
get() = timelineSyncManagerFactory.get()
protected val gifShareProvider: GifShareProvider?
get() = gifShareProviderFactory.newInstance(context)
private val actionHelper = IBaseFragment.ActionHelper(this)
override fun onViewStateRestored(savedInstanceState: Bundle?) {

View File

@ -405,7 +405,7 @@ class MessageConversationInfoFragment : BaseFragment(), IToolBarSupportFragment,
this.delete_always = true
})
val uploadResult = UpdateStatusTask.uploadMicroBlogMediaShared(context,
upload, account, media, null, true, null)
upload, account, media, null, null, true, null)
deleteAlways = uploadResult.deleteAlways
val avatarId = uploadResult.ids.first()
val result = microBlog.updateDmConversationAvatar(conversationId, avatarId)

View File

@ -48,10 +48,7 @@ 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.contains
import org.mariotaku.ktextension.empty
import org.mariotaku.ktextension.mapToArray
import org.mariotaku.ktextension.set
import org.mariotaku.ktextension.*
import org.mariotaku.pickncrop.library.MediaPickerActivity
import org.mariotaku.sqliteqb.library.Expression
import org.mariotaku.sqliteqb.library.OrderBy
@ -62,6 +59,7 @@ import org.mariotaku.twidere.activity.ThemedMediaPickerActivity
import org.mariotaku.twidere.adapter.MediaPreviewAdapter
import org.mariotaku.twidere.adapter.MessagesConversationAdapter
import org.mariotaku.twidere.adapter.iface.ILoadMoreSupportAdapter
import org.mariotaku.twidere.annotation.AccountType
import org.mariotaku.twidere.constant.IntentConstants.*
import org.mariotaku.twidere.constant.nameFirstKey
import org.mariotaku.twidere.constant.newDocumentApiKey
@ -188,7 +186,7 @@ class MessagesConversationFragment : AbsContentListRecyclerViewFragment<Messages
refreshEnabled = false
adapter.loadMoreSupportedPosition = ILoadMoreSupportAdapter.NONE
if (account.isOfficial(context)) {
if (account.type == AccountType.TWITTER) {
addMedia.visibility = View.VISIBLE
} else {
addMedia.visibility = View.GONE
@ -225,16 +223,33 @@ class MessagesConversationFragment : AbsContentListRecyclerViewFragment<Messages
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
when (requestCode) {
REQUEST_PICK_MEDIA -> {
if (resultCode == Activity.RESULT_OK && data != null) {
when (resultCode) {
Activity.RESULT_OK -> if (data != null) {
val mediaUris = MediaPickerActivity.getMediaUris(data)
TaskStarter.execute(AddMediaTask(this, mediaUris))
val types = data.getBundleExtra(MediaPickerActivity.EXTRA_EXTRAS)?.getIntArray(EXTRA_TYPES)
TaskStarter.execute(AddMediaTask(this, mediaUris, types))
}
RESULT_SEARCH_GIF -> {
val provider = gifShareProvider ?: return
startActivityForResult(provider.createGifSelectorIntent(), REQUEST_ADD_GIF)
}
}
}
REQUEST_MANAGE_CONVERSATION_INFO -> {
if (resultCode == MessageConversationInfoFragment.RESULT_CLOSE) {
activity?.finish()
}
}
REQUEST_ADD_GIF -> {
if (resultCode == Activity.RESULT_OK && data != null) {
val intent = ThemedMediaPickerActivity.withThemed(context)
.getMedia(data.data)
.extras(Bundle { this[EXTRA_TYPES] = intArrayOf(ParcelableMedia.Type.ANIMATED_GIF) })
.build()
startActivityForResult(intent, REQUEST_PICK_MEDIA)
}
}
}
}
@ -390,7 +405,7 @@ class MessagesConversationFragment : AbsContentListRecyclerViewFragment<Messages
editText.error = getString(R.string.hint_error_message_no_content)
return
}
if (conversationAccount.isOfficial(context)) {
if (conversationAccount.type == AccountType.TWITTER) {
if (mediaPreviewAdapter.itemCount > defaultFeatures.twitterDirectMessageMediaLimit) {
editText.error = getString(R.string.error_message_media_message_too_many)
return
@ -423,14 +438,17 @@ class MessagesConversationFragment : AbsContentListRecyclerViewFragment<Messages
}
private fun openMediaPicker() {
val intent = ThemedMediaPickerActivity.withThemed(context)
.pickSources(arrayOf(MediaPickerActivity.SOURCE_CAMERA,
val builder = ThemedMediaPickerActivity.withThemed(context)
builder.pickSources(arrayOf(MediaPickerActivity.SOURCE_CAMERA,
MediaPickerActivity.SOURCE_CAMCORDER,
MediaPickerActivity.SOURCE_GALLERY,
MediaPickerActivity.SOURCE_CLIPBOARD))
.containsVideo(true)
.allowMultiple(false)
.build()
if (gifShareProvider != null) {
builder.addEntry(getString(R.string.action_add_gif), "gif", RESULT_SEARCH_GIF)
}
builder.containsVideo(true)
builder.allowMultiple(false)
val intent = builder.build()
startActivityForResult(intent, REQUEST_PICK_MEDIA)
}
@ -499,8 +517,9 @@ class MessagesConversationFragment : AbsContentListRecyclerViewFragment<Messages
internal class AddMediaTask(
fragment: MessagesConversationFragment,
sources: Array<Uri>
) : AbsAddMediaTask<MessagesConversationFragment>(fragment.context, sources) {
sources: Array<Uri>,
types: IntArray?
) : AbsAddMediaTask<MessagesConversationFragment>(fragment.context, sources, types) {
init {
callback = fragment
@ -572,6 +591,10 @@ class MessagesConversationFragment : AbsContentListRecyclerViewFragment<Messages
companion object {
private const val REQUEST_MANAGE_CONVERSATION_INFO = 101
private const val REQUEST_ADD_GIF = 102
private const val RESULT_SEARCH_GIF = 11
private const val EXTRA_TYPES = "types"
}
}

View File

@ -335,7 +335,7 @@ class LengthyOperationsService : BaseIntentService("lengthy_operations") {
val mediaType = body.contentType().contentType
val length = body.length()
val stream = body.stream()
var response = upload.initUploadMedia(mediaType, length, null)
var response = upload.initUploadMedia(mediaType, length, null, null)
val segments = if (length == 0L) 0 else (length / BULK_SIZE + 1).toInt()
for (segmentIndex in 0..segments - 1) {
val currentBulkSize = Math.min(BULK_SIZE, length - segmentIndex * BULK_SIZE).toInt()

View File

@ -35,6 +35,7 @@ import java.lang.ref.WeakReference
open class AbsAddMediaTask<Callback>(
context: Context,
val sources: Array<Uri>,
val types: IntArray?,
val copySrc: Boolean = false,
val deleteSrc: Boolean = false
) : AbstractTask<Unit, List<ParcelableMediaUpdate>?, Callback>() {
@ -50,8 +51,9 @@ open class AbsAddMediaTask<Callback>(
var os: OutputStream? = null
try {
val sourceMimeType = resolver.getType(source)
val mediaType = sourceMimeType?.let {
val mediaType = types?.get(index) ?: sourceMimeType?.let {
return@let when {
it == "image/gif" -> ParcelableMedia.Type.ANIMATED_GIF
it.startsWith("video/") -> ParcelableMedia.Type.VIDEO
it.startsWith("image/") -> ParcelableMedia.Type.IMAGE
else -> ParcelableMedia.Type.IMAGE

View File

@ -369,7 +369,7 @@ class UpdateStatusTask(
mediaIds = pendingUpdate.sharedMediaIds
} else {
val (ids, deleteOnSuccess, deleteAlways) = uploadMicroBlogMediaShared(context,
upload, account, update.media, ownerIds, true, stateCallback)
upload, account, update.media, null, ownerIds, true, stateCallback)
mediaIds = ids
deleteOnSuccess.addAllTo(pendingUpdate.deleteOnSuccess)
deleteAlways.addAllTo(pendingUpdate.deleteAlways)
@ -384,7 +384,7 @@ class UpdateStatusTask(
// TODO use their native API
val upload = account.newMicroBlogInstance(context, cls = TwitterUpload::class.java)
val (ids, deleteOnSuccess, deleteAlways) = uploadMicroBlogMediaShared(context,
upload, account, update.media, ownerIds, false, stateCallback)
upload, account, update.media, null, ownerIds, false, stateCallback)
mediaIds = ids
deleteOnSuccess.addAllTo(pendingUpdate.deleteOnSuccess)
deleteAlways.addAllTo(pendingUpdate.deleteAlways)
@ -697,13 +697,13 @@ class UpdateStatusTask(
companion object {
private val BULK_SIZE = 256 * 1024// 128 Kib
private val BULK_SIZE = 512 * 1024// 512 Kib
@Throws(UploadException::class)
fun uploadMicroBlogMediaShared(context: Context, upload: TwitterUpload,
account: AccountDetails, media: Array<ParcelableMediaUpdate>,
ownerIds: Array<String>?, chucked: Boolean, callback: UploadCallback?):
SharedMediaUploadResult {
mediaCategory: String? = null, ownerIds: Array<String>?, chucked: Boolean,
callback: UploadCallback?): SharedMediaUploadResult {
val deleteOnSuccess = ArrayList<MediaDeletionItem>()
val deleteAlways = ArrayList<MediaDeletionItem>()
val mediaIds = media.mapIndexedToArray { index, media ->
@ -717,7 +717,7 @@ class UpdateStatusTask(
callback?.onUploadingProgressChanged(index, position, length)
})
if (chucked) {
resp = uploadMediaChucked(upload, body.body, ownerIds)
resp = uploadMediaChucked(upload, body.body, mediaCategory, ownerIds)
} else {
resp = upload.uploadMedia(body.body, ownerIds)
}
@ -841,11 +841,11 @@ class UpdateStatusTask(
@Throws(IOException::class, MicroBlogException::class)
private fun uploadMediaChucked(upload: TwitterUpload, body: Body,
ownerIds: Array<String>?): MediaUploadResponse {
mediaCategory: String? = null, ownerIds: Array<String>?): MediaUploadResponse {
val mediaType = body.contentType().contentType
val length = body.length()
val stream = body.stream()
var response = upload.initUploadMedia(mediaType, length, ownerIds)
var response = upload.initUploadMedia(mediaType, length, mediaCategory, ownerIds)
val segments = if (length == 0L) 0 else (length / BULK_SIZE + 1).toInt()
for (segmentIndex in 0..segments - 1) {
val currentBulkSize = Math.min(BULK_SIZE.toLong(), length - segmentIndex * BULK_SIZE).toInt()

View File

@ -29,10 +29,11 @@ import org.mariotaku.microblog.library.twitter.model.NewDm
import org.mariotaku.sqliteqb.library.Expression
import org.mariotaku.twidere.R
import org.mariotaku.twidere.annotation.AccountType
import org.mariotaku.twidere.extension.model.api.toParcelable
import org.mariotaku.twidere.extension.model.api.*
import org.mariotaku.twidere.extension.model.isOfficial
import org.mariotaku.twidere.extension.model.newMicroBlogInstance
import org.mariotaku.twidere.model.AccountDetails
import org.mariotaku.twidere.model.ParcelableMedia
import org.mariotaku.twidere.model.ParcelableMessageConversation
import org.mariotaku.twidere.model.ParcelableNewMessage
import org.mariotaku.twidere.model.event.SendMessageTaskEvent
@ -86,6 +87,8 @@ class SendMessageTask(
AccountType.TWITTER -> {
if (account.isOfficial(context)) {
return sendTwitterOfficialDM(microBlog, account, message)
} else {
return sendTwitterMessageEvent(microBlog, account, message)
}
}
AccountType.FANFOU -> {
@ -114,7 +117,7 @@ class SendMessageTask(
if (message.media.isNotNullOrEmpty()) {
val upload = account.newMicroBlogInstance(context, cls = TwitterUpload::class.java)
val uploadResult = UpdateStatusTask.uploadMicroBlogMediaShared(context,
upload, account, message.media, null, true, null)
upload, account, message.media, null, null, true, null)
newDm.setMediaId(uploadResult.ids[0])
deleteAlways = uploadResult.deleteAlways
deleteOnSuccess = uploadResult.deleteOnSuccess
@ -136,6 +139,38 @@ class SendMessageTask(
return GetMessagesTask.createDatabaseUpdateData(context, account, response, profileImageSize)
}
private fun sendTwitterMessageEvent(microBlog: MicroBlog, account: AccountDetails,
message: ParcelableNewMessage): GetMessagesTask.DatabaseUpdateData {
val recipientId = message.recipient_ids.singleOrNull() ?: throw MicroBlogException("No recipient")
val category = when (message.media?.firstOrNull()?.type) {
ParcelableMedia.Type.IMAGE -> "dm_image"
ParcelableMedia.Type.VIDEO -> "dm_video"
ParcelableMedia.Type.ANIMATED_GIF -> "dm_gif"
else -> null
}
val response = uploadMediaThen(account, message, category) { mediaId ->
val obj = DirectMessageEventObject {
type = "message_create"
messageCreate {
target { this.recipientId = recipientId }
messageData {
text = message.text
if (mediaId != null) {
attachment {
type = "media"
media {
id = mediaId
}
}
}
}
}
}
return@uploadMediaThen microBlog.newDirectMessageEvent(obj)
}
return createDatabaseUpdateData(account, microBlog.showDirectMessage(response.event.id))
}
private fun sendFanfouDM(microBlog: MicroBlog, account: AccountDetails, message: ParcelableNewMessage): GetMessagesTask.DatabaseUpdateData {
val recipientId = message.recipient_ids.singleOrNull() ?: throw MicroBlogException("No recipient")
val response = microBlog.sendFanfouDirectMessage(recipientId, message.text)
@ -143,24 +178,34 @@ class SendMessageTask(
}
private fun sendDefaultDM(microBlog: MicroBlog, account: AccountDetails, message: ParcelableNewMessage): GetMessagesTask.DatabaseUpdateData {
var deleteOnSuccess: List<UpdateStatusTask.MediaDeletionItem>? = null
var deleteAlways: List<UpdateStatusTask.MediaDeletionItem>? = null
val recipientId = message.recipient_ids.singleOrNull() ?: throw MicroBlogException("No recipient")
val response = try {
var mediaId: String? = null
if (message.media.isNotNullOrEmpty()) {
val upload = account.newMicroBlogInstance(context, cls = TwitterUpload::class.java)
val uploadResult = UpdateStatusTask.uploadMicroBlogMediaShared(context,
upload, account, message.media, null, true, null)
mediaId = uploadResult.ids[0]
deleteAlways = uploadResult.deleteAlways
deleteOnSuccess = uploadResult.deleteOnSuccess
}
val response = uploadMediaThen(account, message) { mediaId ->
if (mediaId != null) {
microBlog.sendDirectMessage(recipientId, message.text, mediaId)
} else {
microBlog.sendDirectMessage(recipientId, message.text)
}
}
return createDatabaseUpdateData(account, response)
}
private fun <T> uploadMediaThen(account: AccountDetails, message: ParcelableNewMessage,
category: String? = null, action: (mediaId: String?) -> T): T {
var deleteOnSuccess: List<UpdateStatusTask.MediaDeletionItem>? = null
var deleteAlways: List<UpdateStatusTask.MediaDeletionItem>? = null
try {
var mediaId: String? = null
if (message.media.isNotNullOrEmpty()) {
val upload = account.newMicroBlogInstance(context, cls = TwitterUpload::class.java)
val uploadResult = UpdateStatusTask.uploadMicroBlogMediaShared(context,
upload, account, message.media, category, null, true, null)
mediaId = uploadResult.ids[0]
deleteAlways = uploadResult.deleteAlways
deleteOnSuccess = uploadResult.deleteOnSuccess
}
val result = action(mediaId)
deleteOnSuccess?.forEach { it.delete(context) }
return result
} catch (e: UpdateStatusTask.UploadException) {
e.deleteAlways?.forEach {
it.delete(context)
@ -169,8 +214,6 @@ class SendMessageTask(
} finally {
deleteAlways?.forEach { it.delete(context) }
}
deleteOnSuccess?.forEach { it.delete(context) }
return createDatabaseUpdateData(account, response)
}
private fun createDatabaseUpdateData(details: AccountDetails, dm: DirectMessage): GetMessagesTask.DatabaseUpdateData {