supports dm link click
fixed dm media preview improved dm draft
This commit is contained in:
parent
da54c028bd
commit
7bb29e84b5
|
@ -23,6 +23,7 @@ package org.mariotaku.microblog.library.twitter.api;
|
|||
|
||||
import org.mariotaku.microblog.library.MicroBlogException;
|
||||
import org.mariotaku.microblog.library.twitter.model.ConversationTimeline;
|
||||
import org.mariotaku.microblog.library.twitter.model.DMResponse;
|
||||
import org.mariotaku.microblog.library.twitter.model.NewDm;
|
||||
import org.mariotaku.microblog.library.twitter.model.Paging;
|
||||
import org.mariotaku.microblog.library.twitter.model.ResponseCode;
|
||||
|
@ -46,11 +47,7 @@ public interface PrivateDirectMessagesResources extends PrivateResources {
|
|||
ResponseCode destroyDirectMessagesConversation(@Path("conversation_id") String conversationId) throws MicroBlogException;
|
||||
|
||||
@POST("/dm/new.json")
|
||||
ResponseCode sendDm(@Param NewDm newDm) throws MicroBlogException;
|
||||
|
||||
@POST("/dm/conversation/{account_id}-{user_id}/delete.json")
|
||||
@BodyType(BodyType.FORM)
|
||||
ResponseCode destroyDirectMessagesConversation(@Path("account_id") String accountId, @Path("user_id") String userId) throws MicroBlogException;
|
||||
DMResponse sendDm(@Param NewDm newDm) throws MicroBlogException;
|
||||
|
||||
@GET("/dm/user_inbox.json")
|
||||
UserInbox getUserInbox(@Query Paging paging) throws MicroBlogException;
|
||||
|
|
|
@ -0,0 +1,83 @@
|
|||
/*
|
||||
* Twidere - Twitter client for Android
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you 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.twidere.model;
|
||||
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
|
||||
import com.bluelinelabs.logansquare.annotation.JsonField;
|
||||
import com.bluelinelabs.logansquare.annotation.JsonObject;
|
||||
import com.hannesdorfmann.parcelableplease.annotation.ParcelablePlease;
|
||||
import com.hannesdorfmann.parcelableplease.annotation.ParcelableThisPlease;
|
||||
|
||||
/**
|
||||
* Created by mariotaku on 2017/2/14.
|
||||
*/
|
||||
@ParcelablePlease
|
||||
@JsonObject
|
||||
public class ParcelableNewMessage implements Parcelable {
|
||||
|
||||
@JsonField(name = "account")
|
||||
@ParcelableThisPlease
|
||||
public AccountDetails account;
|
||||
@JsonField(name = "conversation_id")
|
||||
@ParcelableThisPlease
|
||||
public String conversation_id;
|
||||
@JsonField(name = "recipient_id")
|
||||
@ParcelableThisPlease
|
||||
public String recipient_id;
|
||||
@JsonField(name = "text")
|
||||
@ParcelableThisPlease
|
||||
public String text;
|
||||
@JsonField(name = "media")
|
||||
@ParcelableThisPlease
|
||||
public ParcelableMediaUpdate[] media;
|
||||
@JsonField(name = "draft_unique_id")
|
||||
@ParcelableThisPlease
|
||||
public String draft_unique_id;
|
||||
@JsonField(name = "draft_action")
|
||||
@ParcelableThisPlease
|
||||
@Draft.Action
|
||||
public String draft_action;
|
||||
|
||||
@Override
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToParcel(Parcel dest, int flags) {
|
||||
ParcelableNewMessageParcelablePlease.writeToParcel(this, dest, flags);
|
||||
}
|
||||
|
||||
public static final Creator<ParcelableNewMessage> CREATOR = new Creator<ParcelableNewMessage>() {
|
||||
public ParcelableNewMessage createFromParcel(Parcel source) {
|
||||
ParcelableNewMessage target = new ParcelableNewMessage();
|
||||
ParcelableNewMessageParcelablePlease.readFromParcel(target, source);
|
||||
return target;
|
||||
}
|
||||
|
||||
public ParcelableNewMessage[] newArray(int size) {
|
||||
return new ParcelableNewMessage[size];
|
||||
}
|
||||
};
|
||||
}
|
|
@ -68,6 +68,10 @@ public class ParcelableStatusUpdate implements Parcelable {
|
|||
@JsonField(name = "draft_unique_id")
|
||||
@ParcelableThisPlease
|
||||
public String draft_unique_id;
|
||||
@JsonField(name = "draft_action")
|
||||
@ParcelableThisPlease
|
||||
@Draft.Action
|
||||
public String draft_action;
|
||||
|
||||
public ParcelableStatusUpdate() {
|
||||
}
|
||||
|
|
|
@ -37,6 +37,9 @@ public class SendDirectMessageActionExtras implements ActionExtras {
|
|||
@ParcelableThisPlease
|
||||
@JsonField(name = "recipient_id")
|
||||
String recipientId;
|
||||
@ParcelableThisPlease
|
||||
@JsonField(name = "conversation_id")
|
||||
String conversationId;
|
||||
|
||||
public String getRecipientId() {
|
||||
return recipientId;
|
||||
|
@ -46,6 +49,14 @@ public class SendDirectMessageActionExtras implements ActionExtras {
|
|||
this.recipientId = recipientId;
|
||||
}
|
||||
|
||||
public String getConversationId() {
|
||||
return conversationId;
|
||||
}
|
||||
|
||||
public void setConversationId(final String conversationId) {
|
||||
this.conversationId = conversationId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
|
|
|
@ -3,10 +3,6 @@ package org.mariotaku.ktextension
|
|||
import android.os.Bundle
|
||||
import android.os.Parcelable
|
||||
|
||||
/**
|
||||
* Created by mariotaku on 16/8/18.
|
||||
*/
|
||||
|
||||
inline fun Bundle(action: Bundle.() -> Unit): Bundle {
|
||||
val bundle = Bundle()
|
||||
action(bundle)
|
||||
|
|
|
@ -0,0 +1,27 @@
|
|||
/*
|
||||
* 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.ktextension
|
||||
|
||||
import android.content.Intent
|
||||
import android.os.Parcelable
|
||||
|
||||
fun <T> Intent.getTypedArrayExtra(key: String, creator: Parcelable.Creator<T>): Array<T> {
|
||||
return getParcelableArrayExtra(key).toTypedArray(creator)
|
||||
}
|
|
@ -33,6 +33,7 @@ import org.mariotaku.twidere.constant.nameFirstKey
|
|||
import org.mariotaku.twidere.extension.model.timestamp
|
||||
import org.mariotaku.twidere.model.*
|
||||
import org.mariotaku.twidere.model.ParcelableMessage.MessageType
|
||||
import org.mariotaku.twidere.util.DirectMessageOnLinkClickHandler
|
||||
import org.mariotaku.twidere.util.MediaLoadingHandler
|
||||
import org.mariotaku.twidere.util.TwidereLinkify
|
||||
import org.mariotaku.twidere.view.holder.message.AbsMessageViewHolder
|
||||
|
@ -50,7 +51,7 @@ class MessagesConversationAdapter(context: Context) : LoadMoreSupportAdapter<Rec
|
|||
val mediaPreviewStyle: Int = preferences[mediaPreviewStyleKey]
|
||||
val linkHighlightingStyle: Int = preferences[linkHighlightOptionKey]
|
||||
val nameFirst: Boolean = preferences[nameFirstKey]
|
||||
val linkify: TwidereLinkify = TwidereLinkify(null)
|
||||
val linkify: TwidereLinkify = TwidereLinkify(DirectMessageOnLinkClickHandler(context, null, preferences))
|
||||
val mediaLoadingHandler: MediaLoadingHandler = MediaLoadingHandler()
|
||||
|
||||
var messages: List<ParcelableMessage>? = null
|
||||
|
|
|
@ -66,6 +66,7 @@ val mediaPreloadOnWifiOnlyKey = KBooleanKey(KEY_PRELOAD_WIFI_ONLY, true)
|
|||
val autoRefreshCompatibilityModeKey = KBooleanKey("auto_refresh_compatibility_mode", Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP)
|
||||
val floatingDetailedContentsKey = KBooleanKey("floating_detailed_contents", true)
|
||||
val localTrendsWoeIdKey = KIntKey(KEY_LOCAL_TRENDS_WOEID, 1)
|
||||
val phishingLinksWaringKey = KBooleanKey(KEY_PHISHING_LINK_WARNING, true)
|
||||
|
||||
object themeBackgroundAlphaKey : KSimpleKey<Int>(KEY_THEME_BACKGROUND_ALPHA, 0xFF) {
|
||||
override fun read(preferences: SharedPreferences): Int {
|
||||
|
|
|
@ -2,9 +2,8 @@ package org.mariotaku.twidere.model.util
|
|||
|
||||
import android.accounts.AccountManager
|
||||
import android.content.Context
|
||||
import org.mariotaku.ktextension.convert
|
||||
import org.mariotaku.twidere.extension.model.unique_id_non_null
|
||||
|
||||
import org.mariotaku.twidere.model.AccountDetails
|
||||
import org.mariotaku.twidere.model.Draft
|
||||
import org.mariotaku.twidere.model.ParcelableStatusUpdate
|
||||
import org.mariotaku.twidere.model.draft.UpdateStatusActionExtras
|
||||
|
@ -16,11 +15,9 @@ object ParcelableStatusUpdateUtils {
|
|||
|
||||
fun fromDraftItem(context: Context, draft: Draft): ParcelableStatusUpdate {
|
||||
val statusUpdate = ParcelableStatusUpdate()
|
||||
if (draft.account_keys != null) {
|
||||
statusUpdate.accounts = AccountUtils.getAllAccountDetails(AccountManager.get(context), draft.account_keys!!, true)
|
||||
} else {
|
||||
statusUpdate.accounts = arrayOfNulls<AccountDetails>(0)
|
||||
}
|
||||
statusUpdate.accounts = draft.account_keys?.convert {
|
||||
AccountUtils.getAllAccountDetails(AccountManager.get(context), it, true)
|
||||
} ?: emptyArray()
|
||||
statusUpdate.text = draft.text
|
||||
statusUpdate.location = draft.location
|
||||
statusUpdate.media = draft.media
|
||||
|
@ -31,6 +28,7 @@ object ParcelableStatusUpdateUtils {
|
|||
statusUpdate.display_coordinates = extra.displayCoordinates
|
||||
statusUpdate.attachment_url = extra.attachmentUrl
|
||||
}
|
||||
statusUpdate.draft_action = draft.action_type
|
||||
statusUpdate.draft_unique_id = draft.unique_id_non_null
|
||||
return statusUpdate
|
||||
}
|
|
@ -19,6 +19,7 @@
|
|||
|
||||
package org.mariotaku.twidere.service
|
||||
|
||||
import android.accounts.AccountManager
|
||||
import android.annotation.SuppressLint
|
||||
import android.app.Notification
|
||||
import android.app.Service
|
||||
|
@ -42,10 +43,7 @@ import nl.komponents.kovenant.task
|
|||
import nl.komponents.kovenant.ui.successUi
|
||||
import org.mariotaku.abstask.library.AbstractTask
|
||||
import org.mariotaku.abstask.library.ManualTaskStarter
|
||||
import org.mariotaku.ktextension.configure
|
||||
import org.mariotaku.ktextension.toLong
|
||||
import org.mariotaku.ktextension.toTypedArray
|
||||
import org.mariotaku.ktextension.useCursor
|
||||
import org.mariotaku.ktextension.*
|
||||
import org.mariotaku.microblog.library.MicroBlogException
|
||||
import org.mariotaku.microblog.library.twitter.TwitterUpload
|
||||
import org.mariotaku.microblog.library.twitter.model.MediaUploadResponse
|
||||
|
@ -59,13 +57,13 @@ import org.mariotaku.twidere.TwidereConstants.*
|
|||
import org.mariotaku.twidere.model.*
|
||||
import org.mariotaku.twidere.model.draft.SendDirectMessageActionExtras
|
||||
import org.mariotaku.twidere.model.draft.StatusObjectExtras
|
||||
import org.mariotaku.twidere.model.util.AccountUtils
|
||||
import org.mariotaku.twidere.model.util.ParcelableStatusUpdateUtils
|
||||
import org.mariotaku.twidere.provider.TwidereDataStore.Drafts
|
||||
import org.mariotaku.twidere.task.CreateFavoriteTask
|
||||
import org.mariotaku.twidere.task.RetweetStatusTask
|
||||
import org.mariotaku.twidere.task.SendMessageTask
|
||||
import org.mariotaku.twidere.task.twitter.UpdateStatusTask
|
||||
import org.mariotaku.twidere.util.ContentValuesCreator
|
||||
import org.mariotaku.twidere.util.NotificationManagerWrapper
|
||||
import org.mariotaku.twidere.util.Utils
|
||||
import org.mariotaku.twidere.util.deleteDrafts
|
||||
|
@ -132,13 +130,21 @@ class LengthyOperationsService : BaseIntentService("lengthy_operations") {
|
|||
when (draft.action_type) {
|
||||
Draft.Action.UPDATE_STATUS_COMPAT_1, Draft.Action.UPDATE_STATUS_COMPAT_2,
|
||||
Draft.Action.UPDATE_STATUS, Draft.Action.REPLY, Draft.Action.QUOTE -> {
|
||||
updateStatuses(draft.action_type, ParcelableStatusUpdateUtils.fromDraftItem(this, draft))
|
||||
updateStatuses(ParcelableStatusUpdateUtils.fromDraftItem(this, draft))
|
||||
}
|
||||
Draft.Action.SEND_DIRECT_MESSAGE_COMPAT, Draft.Action.SEND_DIRECT_MESSAGE -> {
|
||||
val recipientId = (draft.action_extras as? SendDirectMessageActionExtras)?.recipientId ?: return
|
||||
val accountKey = draft.account_keys?.firstOrNull() ?: return
|
||||
val imageUri = draft.media.firstOrNull()?.uri
|
||||
sendMessage(accountKey, recipientId, draft.text, imageUri)
|
||||
val extras = draft.action_extras as? SendDirectMessageActionExtras ?: return
|
||||
val message = ParcelableNewMessage().apply {
|
||||
this.account = draft.account_keys?.firstOrNull()?.convert { key ->
|
||||
val am = AccountManager.get(this@LengthyOperationsService)
|
||||
return@convert AccountUtils.getAccountDetails(am, key, true)
|
||||
}
|
||||
this.text = draft.text
|
||||
this.media = draft.media
|
||||
this.recipient_id = extras.recipientId
|
||||
this.conversation_id = extras.conversationId
|
||||
}
|
||||
sendMessage(message)
|
||||
}
|
||||
Draft.Action.FAVORITE -> {
|
||||
performStatusAction(draft) { accountKey, status ->
|
||||
|
@ -167,22 +173,18 @@ class LengthyOperationsService : BaseIntentService("lengthy_operations") {
|
|||
}
|
||||
|
||||
private fun handleSendDirectMessageIntent(intent: Intent) {
|
||||
val accountId = intent.getParcelableExtra<UserKey>(EXTRA_ACCOUNT_KEY)
|
||||
val recipientId = intent.getStringExtra(EXTRA_RECIPIENT_ID)
|
||||
val text = intent.getStringExtra(EXTRA_TEXT)
|
||||
val imageUri = intent.getStringExtra(EXTRA_IMAGE_URI)
|
||||
if (accountId == null || recipientId == null || text == null) return
|
||||
sendMessage(accountId, recipientId, text, imageUri)
|
||||
val message = intent.getParcelableExtra<ParcelableNewMessage>(EXTRA_MESSAGE) ?: return
|
||||
sendMessage(message)
|
||||
}
|
||||
|
||||
private fun sendMessage(accountId: UserKey, recipientId: String, text: String, imageUri: String?) {
|
||||
private fun sendMessage(message: ParcelableNewMessage) {
|
||||
val title = getString(R.string.sending_direct_message)
|
||||
val builder = Builder(this)
|
||||
builder.setSmallIcon(R.drawable.ic_stat_send)
|
||||
builder.setProgress(100, 0, true)
|
||||
builder.setTicker(title)
|
||||
builder.setContentTitle(title)
|
||||
builder.setContentText(text)
|
||||
builder.setContentText(message.text)
|
||||
builder.setCategory(NotificationCompat.CATEGORY_PROGRESS)
|
||||
builder.setOngoing(true)
|
||||
val notification = builder.build()
|
||||
|
@ -192,12 +194,18 @@ class LengthyOperationsService : BaseIntentService("lengthy_operations") {
|
|||
val result = ManualTaskStarter.invokeExecute(task)
|
||||
invokeAfterExecute(task, result)
|
||||
|
||||
val resolver = contentResolver
|
||||
if (result.hasData()) {
|
||||
showOkMessage(R.string.message_direct_message_sent, false)
|
||||
} else {
|
||||
val values = ContentValuesCreator.createMessageDraft(accountId, recipientId, text, imageUri)
|
||||
resolver.insert(Drafts.CONTENT_URI, values)
|
||||
UpdateStatusTask.saveDraft(this, Draft.Action.SEND_DIRECT_MESSAGE) {
|
||||
account_keys = arrayOf(message.account.key)
|
||||
text = message.text
|
||||
media = message.media
|
||||
action_extras = SendDirectMessageActionExtras().apply {
|
||||
recipientId = message.recipient_id
|
||||
conversationId = message.conversation_id
|
||||
}
|
||||
}
|
||||
showErrorMessage(R.string.action_sending_direct_message, result.exception, true)
|
||||
}
|
||||
stopForeground(false)
|
||||
|
@ -216,10 +224,11 @@ class LengthyOperationsService : BaseIntentService("lengthy_operations") {
|
|||
return
|
||||
@Draft.Action
|
||||
val actionType = intent.getStringExtra(EXTRA_ACTION)
|
||||
updateStatuses(actionType, *statuses)
|
||||
statuses.forEach { it.draft_action = actionType }
|
||||
updateStatuses(*statuses)
|
||||
}
|
||||
|
||||
private fun updateStatuses(@Draft.Action actionType: String, vararg statuses: ParcelableStatusUpdate) {
|
||||
private fun updateStatuses(vararg statuses: ParcelableStatusUpdate) {
|
||||
val context = this
|
||||
val builder = Builder(context)
|
||||
startForeground(NOTIFICATION_ID_UPDATE_STATUS, updateUpdateStatusNotification(context,
|
||||
|
@ -289,7 +298,7 @@ class LengthyOperationsService : BaseIntentService("lengthy_operations") {
|
|||
}
|
||||
})
|
||||
task.callback = this
|
||||
task.params = Pair(actionType, item)
|
||||
task.params = item
|
||||
invokeBeforeExecute(task)
|
||||
|
||||
val result = ManualTaskStarter.invokeExecute(task)
|
||||
|
@ -387,7 +396,7 @@ class LengthyOperationsService : BaseIntentService("lengthy_operations") {
|
|||
}
|
||||
|
||||
internal class MessageMediaUploadListener(private val context: Context, private val manager: NotificationManagerWrapper,
|
||||
builder: NotificationCompat.Builder, private val message: String) : ReadListener {
|
||||
builder: NotificationCompat.Builder, private val message: String) : ReadListener {
|
||||
|
||||
var percent: Int = 0
|
||||
|
||||
|
@ -411,8 +420,8 @@ class LengthyOperationsService : BaseIntentService("lengthy_operations") {
|
|||
private val BULK_SIZE = (128 * 1024).toLong() // 128KiB
|
||||
|
||||
private fun updateSendDirectMessageNotification(context: Context,
|
||||
builder: NotificationCompat.Builder,
|
||||
progress: Int, message: String?): Notification {
|
||||
builder: NotificationCompat.Builder,
|
||||
progress: Int, message: String?): Notification {
|
||||
builder.setContentTitle(context.getString(R.string.sending_direct_message))
|
||||
if (message != null) {
|
||||
builder.setContentText(message)
|
||||
|
@ -424,9 +433,9 @@ class LengthyOperationsService : BaseIntentService("lengthy_operations") {
|
|||
}
|
||||
|
||||
private fun updateUpdateStatusNotification(context: Context,
|
||||
builder: NotificationCompat.Builder,
|
||||
progress: Int,
|
||||
status: ParcelableStatusUpdate?): Notification {
|
||||
builder: NotificationCompat.Builder,
|
||||
progress: Int,
|
||||
status: ParcelableStatusUpdate?): Notification {
|
||||
builder.setContentTitle(context.getString(R.string.updating_status_notification))
|
||||
if (status != null) {
|
||||
builder.setContentText(status.text)
|
||||
|
@ -438,7 +447,7 @@ class LengthyOperationsService : BaseIntentService("lengthy_operations") {
|
|||
}
|
||||
|
||||
fun updateStatusesAsync(context: Context, @Draft.Action action: String,
|
||||
vararg statuses: ParcelableStatusUpdate) {
|
||||
vararg statuses: ParcelableStatusUpdate) {
|
||||
val intent = Intent(context, LengthyOperationsService::class.java)
|
||||
intent.action = INTENT_ACTION_UPDATE_STATUS
|
||||
intent.putExtra(EXTRA_STATUSES, statuses)
|
||||
|
|
|
@ -50,7 +50,7 @@ class GetMessagesTask(
|
|||
} catch (e: MicroBlogException) {
|
||||
return@forEachIndexed
|
||||
}
|
||||
storeMessages(messages, details)
|
||||
storeMessages(context, messages, details)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -59,7 +59,7 @@ class GetMessagesTask(
|
|||
bus.post(GetMessagesTaskEvent(Messages.CONTENT_URI, false, null))
|
||||
}
|
||||
|
||||
private fun getMessages(microBlog: MicroBlog, details: AccountDetails, param: RefreshMessagesTaskParam, index: Int): GetMessagesData {
|
||||
private fun getMessages(microBlog: MicroBlog, details: AccountDetails, param: RefreshMessagesTaskParam, index: Int): DatabaseUpdateData {
|
||||
when (details.type) {
|
||||
AccountType.FANFOU -> {
|
||||
// Use fanfou DM api
|
||||
|
@ -77,25 +77,25 @@ class GetMessagesTask(
|
|||
}
|
||||
|
||||
private fun getTwitterOfficialMessages(microBlog: MicroBlog, details: AccountDetails,
|
||||
param: RefreshMessagesTaskParam, index: Int): GetMessagesData {
|
||||
param: RefreshMessagesTaskParam, index: Int): DatabaseUpdateData {
|
||||
val conversationId = param.conversationId
|
||||
if (conversationId == null) {
|
||||
return getTwitterOfficialUserInbox(microBlog, details, param, index)
|
||||
} else {
|
||||
return GetMessagesData(emptyList(), emptyList())
|
||||
return DatabaseUpdateData(emptyList(), emptyList())
|
||||
}
|
||||
}
|
||||
|
||||
private fun getFanfouMessages(microBlog: MicroBlog, details: AccountDetails, param: RefreshMessagesTaskParam, index: Int): GetMessagesData {
|
||||
private fun getFanfouMessages(microBlog: MicroBlog, details: AccountDetails, param: RefreshMessagesTaskParam, index: Int): DatabaseUpdateData {
|
||||
val conversationId = param.conversationId
|
||||
if (conversationId == null) {
|
||||
return getFanfouConversations(microBlog, details, param, index)
|
||||
} else {
|
||||
return GetMessagesData(emptyList(), emptyList())
|
||||
return DatabaseUpdateData(emptyList(), emptyList())
|
||||
}
|
||||
}
|
||||
|
||||
private fun getDefaultMessages(microBlog: MicroBlog, details: AccountDetails, param: RefreshMessagesTaskParam, index: Int): GetMessagesData {
|
||||
private fun getDefaultMessages(microBlog: MicroBlog, details: AccountDetails, param: RefreshMessagesTaskParam, index: Int): DatabaseUpdateData {
|
||||
val accountKey = details.key
|
||||
|
||||
val sinceIds = if (param.hasSinceIds) param.sinceIds else null
|
||||
|
@ -137,7 +137,7 @@ class GetMessagesTask(
|
|||
conversationIds.add(ParcelableMessageUtils.outgoingConversationId(it.senderId, it.recipientId))
|
||||
}
|
||||
|
||||
conversations.addLocalConversations(accountKey, conversationIds)
|
||||
conversations.addLocalConversations(context, accountKey, conversationIds)
|
||||
|
||||
received.forEachIndexed { i, dm ->
|
||||
val message = ParcelableMessageUtils.fromMessage(accountKey, dm, false,
|
||||
|
@ -151,7 +151,7 @@ class GetMessagesTask(
|
|||
insertMessages.add(message)
|
||||
conversations.addConversation(message.conversation_id, details, message, setOf(dm.sender, dm.recipient))
|
||||
}
|
||||
return GetMessagesData(conversations.values, insertMessages)
|
||||
return DatabaseUpdateData(conversations.values, insertMessages)
|
||||
}
|
||||
|
||||
private fun getTwitterOfficialConversation(microBlog: MicroBlog, details: AccountDetails,
|
||||
|
@ -159,8 +159,7 @@ class GetMessagesTask(
|
|||
|
||||
}
|
||||
|
||||
private fun getTwitterOfficialUserInbox(microBlog: MicroBlog, details: AccountDetails, param: RefreshMessagesTaskParam, index: Int): GetMessagesData {
|
||||
val accountKey = details.key
|
||||
private fun getTwitterOfficialUserInbox(microBlog: MicroBlog, details: AccountDetails, param: RefreshMessagesTaskParam, index: Int): DatabaseUpdateData {
|
||||
val maxId = if (param.hasMaxIds) param.maxIds?.get(index) else null
|
||||
val cursor = if (param.hasCursors) param.cursors?.get(index) else null
|
||||
val response = if (cursor != null) {
|
||||
|
@ -173,64 +172,11 @@ class GetMessagesTask(
|
|||
}).userInbox
|
||||
}
|
||||
|
||||
val respConversations = response.conversations.orEmpty()
|
||||
val respEntries = response.entries.orEmpty()
|
||||
val respUsers = response.users.orEmpty()
|
||||
|
||||
val conversations = hashMapOf<String, ParcelableMessageConversation>()
|
||||
|
||||
respConversations.keys.let {
|
||||
conversations.addLocalConversations(accountKey, it)
|
||||
}
|
||||
val messages = ArrayList<ParcelableMessage>()
|
||||
val messageDeletionsMap = HashMap<String, ArrayList<String>>()
|
||||
val conversationDeletions = ArrayList<String>()
|
||||
respEntries.mapNotNullTo(messages) { entry ->
|
||||
when {
|
||||
entry.messageDelete != null -> {
|
||||
val list = messageDeletionsMap.getOrPut(entry.messageDelete.conversationId) { ArrayList<String>() }
|
||||
entry.messageDelete.messages?.forEach {
|
||||
list.add(it.messageId)
|
||||
}
|
||||
return@mapNotNullTo null
|
||||
}
|
||||
entry.removeConversation != null -> {
|
||||
conversationDeletions.add(entry.removeConversation.conversationId)
|
||||
return@mapNotNullTo null
|
||||
}
|
||||
else -> {
|
||||
return@mapNotNullTo ParcelableMessageUtils.fromEntry(accountKey, entry, respUsers)
|
||||
}
|
||||
}
|
||||
}
|
||||
val messagesMap = messages.groupBy(ParcelableMessage::conversation_id)
|
||||
for ((k, v) in respConversations) {
|
||||
val message = messagesMap[k]?.maxBy(ParcelableMessage::message_timestamp) ?: continue
|
||||
val participants = respUsers.filterKeys { userId ->
|
||||
v.participants.any { it.userId == userId }
|
||||
}.values
|
||||
val conversationType = when (v.type?.toUpperCase(Locale.US)) {
|
||||
DMResponse.Conversation.Type.ONE_TO_ONE -> ConversationType.ONE_TO_ONE
|
||||
DMResponse.Conversation.Type.GROUP_DM -> ConversationType.GROUP
|
||||
else -> ConversationType.ONE_TO_ONE
|
||||
}
|
||||
val conversation = conversations.addConversation(k, details, message, participants,
|
||||
conversationType)
|
||||
conversation.conversation_name = v.name
|
||||
conversation.conversation_avatar = v.avatarImageHttps
|
||||
conversation.request_cursor = response.cursor
|
||||
conversation.conversation_extras_type = ParcelableMessageConversation.ExtrasType.TWITTER_OFFICIAL
|
||||
conversation.conversation_extras = TwitterOfficialConversationExtras().apply {
|
||||
this.minEntryId = v.minEntryId
|
||||
this.maxEntryId = v.maxEntryId
|
||||
this.status = v.status
|
||||
}
|
||||
}
|
||||
return GetMessagesData(conversations.values, messages, conversationDeletions,
|
||||
messageDeletionsMap, response.cursor)
|
||||
return createDatabaseUpdateData(context, details, response)
|
||||
}
|
||||
|
||||
private fun getFanfouConversations(microBlog: MicroBlog, details: AccountDetails, param: RefreshMessagesTaskParam, index: Int): GetMessagesData {
|
||||
|
||||
private fun getFanfouConversations(microBlog: MicroBlog, details: AccountDetails, param: RefreshMessagesTaskParam, index: Int): DatabaseUpdateData {
|
||||
val accountKey = details.key
|
||||
val cursor = param.cursors?.get(index)
|
||||
val page = cursor?.substringAfter("page:").toInt(-1)
|
||||
|
@ -244,7 +190,7 @@ class GetMessagesTask(
|
|||
|
||||
val conversationIds = hashSetOf<String>()
|
||||
result.mapTo(conversationIds) { "${accountKey.id}-${it.otherId}" }
|
||||
conversations.addLocalConversations(accountKey, conversationIds)
|
||||
conversations.addLocalConversations(context, accountKey, conversationIds)
|
||||
result.forEachIndexed { i, item ->
|
||||
val dm = item.dm
|
||||
// Sender is our self, treat as outgoing message
|
||||
|
@ -254,116 +200,10 @@ class GetMessagesTask(
|
|||
setOf(dm.sender, dm.recipient))
|
||||
mc.request_cursor = "page:$page"
|
||||
}
|
||||
return GetMessagesData(conversations.values, emptyList())
|
||||
return DatabaseUpdateData(conversations.values, emptyList())
|
||||
}
|
||||
|
||||
@SuppressLint("Recycle")
|
||||
private fun MutableMap<String, ParcelableMessageConversation>.addLocalConversations(accountKey: UserKey, conversationIds: Set<String>) {
|
||||
val where = Expression.and(Expression.inArgs(Conversations.CONVERSATION_ID, conversationIds.size),
|
||||
Expression.equalsArgs(Conversations.ACCOUNT_KEY)).sql
|
||||
val whereArgs = conversationIds.toTypedArray() + accountKey.toString()
|
||||
return context.contentResolver.query(Conversations.CONTENT_URI, Conversations.COLUMNS,
|
||||
where, whereArgs, null).useCursor { cur ->
|
||||
val indices = ParcelableMessageConversationCursorIndices(cur)
|
||||
cur.moveToFirst()
|
||||
while (!cur.isAfterLast) {
|
||||
val conversationId = cur.getString(indices.id)
|
||||
val timestamp = cur.getLong(indices.local_timestamp)
|
||||
val conversation = this[conversationId] ?: run {
|
||||
val obj = indices.newObject(cur)
|
||||
this[conversationId] = obj
|
||||
return@run obj
|
||||
}
|
||||
if (timestamp > conversation.local_timestamp) {
|
||||
this[conversationId] = indices.newObject(cur)
|
||||
}
|
||||
indices.newObject(cur)
|
||||
cur.moveToNext()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun ParcelableMessageConversation.addParticipant(
|
||||
accountKey: UserKey,
|
||||
user: User
|
||||
) {
|
||||
val userKey = UserKeyUtils.fromUser(user)
|
||||
val participants = this.participants
|
||||
if (participants == null) {
|
||||
this.participants = arrayOf(ParcelableUserUtils.fromUser(user, accountKey))
|
||||
} else {
|
||||
val index = participants.indexOfFirst { it.key == userKey }
|
||||
if (index >= 0) {
|
||||
participants[index] = ParcelableUserUtils.fromUser(user, accountKey)
|
||||
} else {
|
||||
this.participants = participants + ParcelableUserUtils.fromUser(user, accountKey)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun MutableMap<String, ParcelableMessageConversation>.addConversation(
|
||||
conversationId: String,
|
||||
details: AccountDetails,
|
||||
message: ParcelableMessage,
|
||||
users: Collection<User>,
|
||||
conversationType: String = ConversationType.ONE_TO_ONE
|
||||
): ParcelableMessageConversation {
|
||||
val conversation = this[conversationId] ?: run {
|
||||
val obj = ParcelableMessageConversation()
|
||||
obj.id = conversationId
|
||||
obj.conversation_type = conversationType
|
||||
obj.applyFrom(message, details)
|
||||
this[conversationId] = obj
|
||||
return@run obj
|
||||
}
|
||||
if (message.timestamp > conversation.timestamp) {
|
||||
conversation.applyFrom(message, details)
|
||||
}
|
||||
users.forEach { user ->
|
||||
conversation.addParticipant(details.key, user)
|
||||
}
|
||||
return conversation
|
||||
}
|
||||
|
||||
private fun storeMessages(data: GetMessagesData, details: AccountDetails) {
|
||||
val resolver = context.contentResolver
|
||||
val conversationsValues = data.conversations.map {
|
||||
val values = ParcelableMessageConversationValuesCreator.create(it)
|
||||
if (it._id > 0) {
|
||||
values.put(Conversations._ID, it._id)
|
||||
}
|
||||
return@map values
|
||||
}
|
||||
val messagesValues = data.messages.map(ParcelableMessageValuesCreator::create)
|
||||
|
||||
for ((conversationId, messageIds) in data.deleteMessages) {
|
||||
val where = Expression.and(Expression.equalsArgs(Messages.ACCOUNT_KEY),
|
||||
Expression.equalsArgs(Messages.CONVERSATION_ID)).sql
|
||||
val whereArgs = arrayOf(details.key.toString(), conversationId)
|
||||
ContentResolverUtils.bulkDelete(resolver, Messages.CONTENT_URI, Messages.MESSAGE_ID,
|
||||
false, messageIds, where, whereArgs)
|
||||
}
|
||||
|
||||
val accountWhere = Expression.equalsArgs(Messages.ACCOUNT_KEY).sql
|
||||
val accountWhereArgs = arrayOf(details.key.toString())
|
||||
|
||||
ContentResolverUtils.bulkDelete(resolver, Conversations.CONTENT_URI, Conversations.CONVERSATION_ID,
|
||||
false, data.deleteConversations, accountWhere, accountWhereArgs)
|
||||
ContentResolverUtils.bulkDelete(resolver, Messages.CONTENT_URI, Messages.CONVERSATION_ID,
|
||||
false, data.deleteConversations, accountWhere, accountWhereArgs)
|
||||
|
||||
ContentResolverUtils.bulkInsert(resolver, Conversations.CONTENT_URI, conversationsValues)
|
||||
ContentResolverUtils.bulkInsert(resolver, Messages.CONTENT_URI, messagesValues)
|
||||
|
||||
if (data.conversationRequestCursor != null) {
|
||||
resolver.update(Conversations.CONTENT_URI, ContentValues().apply {
|
||||
put(Conversations.REQUEST_CURSOR, data.conversationRequestCursor)
|
||||
}, accountWhere, accountWhereArgs)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
data class GetMessagesData(
|
||||
data class DatabaseUpdateData(
|
||||
val conversations: Collection<ParcelableMessageConversation>,
|
||||
val messages: Collection<ParcelableMessage>,
|
||||
val deleteConversations: List<String> = emptyList(),
|
||||
|
@ -462,5 +302,175 @@ class GetMessagesTask(
|
|||
get() = getAccountKeys()
|
||||
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
fun createDatabaseUpdateData(context: Context, account: AccountDetails,
|
||||
response: DMResponse): DatabaseUpdateData {
|
||||
val respConversations = response.conversations.orEmpty()
|
||||
val respEntries = response.entries.orEmpty()
|
||||
val respUsers = response.users.orEmpty()
|
||||
|
||||
val conversations = hashMapOf<String, ParcelableMessageConversation>()
|
||||
|
||||
respConversations.keys.let {
|
||||
conversations.addLocalConversations(context, account.key, it)
|
||||
}
|
||||
val messages = ArrayList<ParcelableMessage>()
|
||||
val messageDeletionsMap = HashMap<String, ArrayList<String>>()
|
||||
val conversationDeletions = ArrayList<String>()
|
||||
respEntries.mapNotNullTo(messages) { entry ->
|
||||
when {
|
||||
entry.messageDelete != null -> {
|
||||
val list = messageDeletionsMap.getOrPut(entry.messageDelete.conversationId) { ArrayList<String>() }
|
||||
entry.messageDelete.messages?.forEach {
|
||||
list.add(it.messageId)
|
||||
}
|
||||
return@mapNotNullTo null
|
||||
}
|
||||
entry.removeConversation != null -> {
|
||||
conversationDeletions.add(entry.removeConversation.conversationId)
|
||||
return@mapNotNullTo null
|
||||
}
|
||||
else -> {
|
||||
return@mapNotNullTo ParcelableMessageUtils.fromEntry(account.key, entry, respUsers)
|
||||
}
|
||||
}
|
||||
}
|
||||
val messagesMap = messages.groupBy(ParcelableMessage::conversation_id)
|
||||
for ((k, v) in respConversations) {
|
||||
val message = messagesMap[k]?.maxBy(ParcelableMessage::message_timestamp) ?: continue
|
||||
val participants = respUsers.filterKeys { userId ->
|
||||
v.participants.any { it.userId == userId }
|
||||
}.values
|
||||
val conversationType = when (v.type?.toUpperCase(Locale.US)) {
|
||||
DMResponse.Conversation.Type.ONE_TO_ONE -> ConversationType.ONE_TO_ONE
|
||||
DMResponse.Conversation.Type.GROUP_DM -> ConversationType.GROUP
|
||||
else -> ConversationType.ONE_TO_ONE
|
||||
}
|
||||
val conversation = conversations.addConversation(k, account, message, participants,
|
||||
conversationType)
|
||||
conversation.conversation_name = v.name
|
||||
conversation.conversation_avatar = v.avatarImageHttps
|
||||
conversation.request_cursor = response.cursor
|
||||
conversation.conversation_extras_type = ParcelableMessageConversation.ExtrasType.TWITTER_OFFICIAL
|
||||
conversation.conversation_extras = TwitterOfficialConversationExtras().apply {
|
||||
this.minEntryId = v.minEntryId
|
||||
this.maxEntryId = v.maxEntryId
|
||||
this.status = v.status
|
||||
}
|
||||
}
|
||||
return DatabaseUpdateData(conversations.values, messages, conversationDeletions,
|
||||
messageDeletionsMap, response.cursor)
|
||||
}
|
||||
|
||||
fun storeMessages(context: Context, data: DatabaseUpdateData, details: AccountDetails) {
|
||||
val resolver = context.contentResolver
|
||||
val conversationsValues = data.conversations.map {
|
||||
val values = ParcelableMessageConversationValuesCreator.create(it)
|
||||
if (it._id > 0) {
|
||||
values.put(Conversations._ID, it._id)
|
||||
}
|
||||
return@map values
|
||||
}
|
||||
val messagesValues = data.messages.map(ParcelableMessageValuesCreator::create)
|
||||
|
||||
for ((conversationId, messageIds) in data.deleteMessages) {
|
||||
val where = Expression.and(Expression.equalsArgs(Messages.ACCOUNT_KEY),
|
||||
Expression.equalsArgs(Messages.CONVERSATION_ID)).sql
|
||||
val whereArgs = arrayOf(details.key.toString(), conversationId)
|
||||
ContentResolverUtils.bulkDelete(resolver, Messages.CONTENT_URI, Messages.MESSAGE_ID,
|
||||
false, messageIds, where, whereArgs)
|
||||
}
|
||||
|
||||
val accountWhere = Expression.equalsArgs(Messages.ACCOUNT_KEY).sql
|
||||
val accountWhereArgs = arrayOf(details.key.toString())
|
||||
|
||||
ContentResolverUtils.bulkDelete(resolver, Conversations.CONTENT_URI, Conversations.CONVERSATION_ID,
|
||||
false, data.deleteConversations, accountWhere, accountWhereArgs)
|
||||
ContentResolverUtils.bulkDelete(resolver, Messages.CONTENT_URI, Messages.CONVERSATION_ID,
|
||||
false, data.deleteConversations, accountWhere, accountWhereArgs)
|
||||
|
||||
ContentResolverUtils.bulkInsert(resolver, Conversations.CONTENT_URI, conversationsValues)
|
||||
ContentResolverUtils.bulkInsert(resolver, Messages.CONTENT_URI, messagesValues)
|
||||
|
||||
if (data.conversationRequestCursor != null) {
|
||||
resolver.update(Conversations.CONTENT_URI, ContentValues().apply {
|
||||
put(Conversations.REQUEST_CURSOR, data.conversationRequestCursor)
|
||||
}, accountWhere, accountWhereArgs)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@SuppressLint("Recycle")
|
||||
fun MutableMap<String, ParcelableMessageConversation>.addLocalConversations(context: Context,
|
||||
accountKey: UserKey, conversationIds: Set<String>) {
|
||||
val where = Expression.and(Expression.inArgs(Conversations.CONVERSATION_ID, conversationIds.size),
|
||||
Expression.equalsArgs(Conversations.ACCOUNT_KEY)).sql
|
||||
val whereArgs = conversationIds.toTypedArray() + accountKey.toString()
|
||||
return context.contentResolver.query(Conversations.CONTENT_URI, Conversations.COLUMNS,
|
||||
where, whereArgs, null).useCursor { cur ->
|
||||
val indices = ParcelableMessageConversationCursorIndices(cur)
|
||||
cur.moveToFirst()
|
||||
while (!cur.isAfterLast) {
|
||||
val conversationId = cur.getString(indices.id)
|
||||
val timestamp = cur.getLong(indices.local_timestamp)
|
||||
val conversation = this[conversationId] ?: run {
|
||||
val obj = indices.newObject(cur)
|
||||
this[conversationId] = obj
|
||||
return@run obj
|
||||
}
|
||||
if (timestamp > conversation.local_timestamp) {
|
||||
this[conversationId] = indices.newObject(cur)
|
||||
}
|
||||
indices.newObject(cur)
|
||||
cur.moveToNext()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun ParcelableMessageConversation.addParticipant(
|
||||
accountKey: UserKey,
|
||||
user: User
|
||||
) {
|
||||
val userKey = UserKeyUtils.fromUser(user)
|
||||
val participants = this.participants
|
||||
if (participants == null) {
|
||||
this.participants = arrayOf(ParcelableUserUtils.fromUser(user, accountKey))
|
||||
} else {
|
||||
val index = participants.indexOfFirst { it.key == userKey }
|
||||
if (index >= 0) {
|
||||
participants[index] = ParcelableUserUtils.fromUser(user, accountKey)
|
||||
} else {
|
||||
this.participants = participants + ParcelableUserUtils.fromUser(user, accountKey)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun MutableMap<String, ParcelableMessageConversation>.addConversation(
|
||||
conversationId: String,
|
||||
details: AccountDetails,
|
||||
message: ParcelableMessage,
|
||||
users: Collection<User>,
|
||||
conversationType: String = ConversationType.ONE_TO_ONE
|
||||
): ParcelableMessageConversation {
|
||||
val conversation = this[conversationId] ?: run {
|
||||
val obj = ParcelableMessageConversation()
|
||||
obj.id = conversationId
|
||||
obj.conversation_type = conversationType
|
||||
obj.applyFrom(message, details)
|
||||
this[conversationId] = obj
|
||||
return@run obj
|
||||
}
|
||||
if (message.timestamp > conversation.timestamp) {
|
||||
conversation.applyFrom(message, details)
|
||||
}
|
||||
users.forEach { user ->
|
||||
conversation.addParticipant(details.key, user)
|
||||
}
|
||||
return conversation
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,17 +1,72 @@
|
|||
package org.mariotaku.twidere.task
|
||||
|
||||
import android.content.Context
|
||||
import org.mariotaku.twidere.model.ParcelableMessage
|
||||
import org.mariotaku.twidere.model.SingleResponse
|
||||
import org.mariotaku.microblog.library.MicroBlog
|
||||
import org.mariotaku.microblog.library.MicroBlogException
|
||||
import org.mariotaku.microblog.library.twitter.model.DirectMessage
|
||||
import org.mariotaku.microblog.library.twitter.model.NewDm
|
||||
import org.mariotaku.twidere.annotation.AccountType
|
||||
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.ParcelableMessageConversation
|
||||
import org.mariotaku.twidere.model.ParcelableNewMessage
|
||||
import org.mariotaku.twidere.model.util.ParcelableMessageUtils
|
||||
import org.mariotaku.twidere.task.GetMessagesTask.Companion.addConversation
|
||||
import org.mariotaku.twidere.task.GetMessagesTask.Companion.addLocalConversations
|
||||
|
||||
/**
|
||||
* Created by mariotaku on 2017/2/8.
|
||||
*/
|
||||
class SendMessageTask(
|
||||
context: Context
|
||||
) : BaseAbstractTask<Unit, SingleResponse<ParcelableMessage>, Unit>(context) {
|
||||
override fun doLongOperation(params: Unit?): SingleResponse<ParcelableMessage> {
|
||||
return SingleResponse(UnsupportedOperationException())
|
||||
) : ExceptionHandlingAbstractTask<ParcelableNewMessage, Unit, MicroBlogException, Unit>(context) {
|
||||
override fun onExecute(params: ParcelableNewMessage) {
|
||||
val account = params.account
|
||||
val microBlog = account.newMicroBlogInstance(context, cls = MicroBlog::class.java)
|
||||
val updateData = requestSendMessage(microBlog, account, params)
|
||||
GetMessagesTask.storeMessages(context, updateData, account)
|
||||
}
|
||||
|
||||
fun requestSendMessage(microBlog: MicroBlog, account: AccountDetails, message: ParcelableNewMessage): GetMessagesTask.DatabaseUpdateData {
|
||||
when (account.type) {
|
||||
AccountType.TWITTER -> {
|
||||
if (account.isOfficial(context)) {
|
||||
return sendTwitterOfficialDM(microBlog, account, message)
|
||||
}
|
||||
}
|
||||
AccountType.FANFOU -> {
|
||||
return sendFanfouDM(microBlog, account, message)
|
||||
}
|
||||
}
|
||||
return sendDefaultDM(microBlog, account, message)
|
||||
}
|
||||
|
||||
private fun sendTwitterOfficialDM(microBlog: MicroBlog, account: AccountDetails, message: ParcelableNewMessage): GetMessagesTask.DatabaseUpdateData {
|
||||
val response = microBlog.sendDm(NewDm().apply {
|
||||
setConversationId(message.conversation_id)
|
||||
setText(message.text)
|
||||
})
|
||||
return GetMessagesTask.createDatabaseUpdateData(context, account, response)
|
||||
}
|
||||
|
||||
private fun sendFanfouDM(microBlog: MicroBlog, account: AccountDetails, message: ParcelableNewMessage): GetMessagesTask.DatabaseUpdateData {
|
||||
val response = microBlog.sendFanfouDirectMessage(message.recipient_id, message.text)
|
||||
return createDatabaseUpdateData(account, response)
|
||||
}
|
||||
|
||||
private fun sendDefaultDM(microBlog: MicroBlog, account: AccountDetails, message: ParcelableNewMessage): GetMessagesTask.DatabaseUpdateData {
|
||||
val response = microBlog.sendDirectMessage(message.recipient_id, message.text)
|
||||
return createDatabaseUpdateData(account, response)
|
||||
}
|
||||
|
||||
private fun createDatabaseUpdateData(details: AccountDetails, dm: DirectMessage): GetMessagesTask.DatabaseUpdateData {
|
||||
val accountKey = details.key
|
||||
val conversationIds = setOf(ParcelableMessageUtils.outgoingConversationId(dm.senderId, dm.recipientId))
|
||||
val conversations = hashMapOf<String, ParcelableMessageConversation>()
|
||||
conversations.addLocalConversations(context, accountKey, conversationIds)
|
||||
val message = ParcelableMessageUtils.fromMessage(accountKey, dm, true)
|
||||
conversations.addConversation(message.conversation_id, details, message, setOf(dm.sender, dm.recipient))
|
||||
return GetMessagesTask.DatabaseUpdateData(conversations.values, listOf(message))
|
||||
}
|
||||
}
|
|
@ -62,14 +62,14 @@ import java.util.concurrent.TimeUnit
|
|||
class UpdateStatusTask(
|
||||
context: Context,
|
||||
internal val stateCallback: UpdateStatusTask.StateCallback
|
||||
) : BaseAbstractTask<Pair<String, ParcelableStatusUpdate>, UpdateStatusTask.UpdateStatusResult, Any?>(context) {
|
||||
) : BaseAbstractTask<ParcelableStatusUpdate, UpdateStatusTask.UpdateStatusResult, Any?>(context) {
|
||||
|
||||
override fun doLongOperation(params: Pair<String, ParcelableStatusUpdate>): UpdateStatusResult {
|
||||
val draftId = saveDraft(params.first, params.second)
|
||||
override fun doLongOperation(params: ParcelableStatusUpdate): UpdateStatusResult {
|
||||
val draftId = saveDraft(params)
|
||||
microBlogWrapper.addSendingDraftId(draftId)
|
||||
try {
|
||||
val result = doUpdateStatus(params.second, draftId)
|
||||
deleteOrUpdateDraft(params.second, result, draftId)
|
||||
val result = doUpdateStatus(params, draftId)
|
||||
deleteOrUpdateDraft(params, result, draftId)
|
||||
return result
|
||||
} catch (e: UpdateStatusException) {
|
||||
return UpdateStatusResult(e, draftId)
|
||||
|
@ -85,16 +85,17 @@ class UpdateStatusTask(
|
|||
override fun afterExecute(handler: Any?, result: UpdateStatusResult) {
|
||||
stateCallback.afterExecute(result)
|
||||
if (params != null) {
|
||||
logUpdateStatus(params.first, params.second, result)
|
||||
logUpdateStatus(params, result)
|
||||
}
|
||||
}
|
||||
|
||||
private fun logUpdateStatus(actionType: String, statusUpdate: ParcelableStatusUpdate, result: UpdateStatusResult) {
|
||||
private fun logUpdateStatus(statusUpdate: ParcelableStatusUpdate, result: UpdateStatusResult) {
|
||||
val mediaType = statusUpdate.media?.firstOrNull()?.type ?: ParcelableMedia.Type.UNKNOWN
|
||||
val hasLocation = statusUpdate.location != null
|
||||
val preciseLocation = statusUpdate.display_coordinates
|
||||
Analyzer.log(UpdateStatus(result.accountTypes.firstOrNull(), actionType, mediaType,
|
||||
hasLocation, preciseLocation, result.succeed, result.exceptions.firstOrNull() ?: result.exception))
|
||||
Analyzer.log(UpdateStatus(result.accountTypes.firstOrNull(), statusUpdate.draft_action,
|
||||
mediaType, hasLocation, preciseLocation, result.succeed,
|
||||
result.exceptions.firstOrNull() ?: result.exception))
|
||||
}
|
||||
|
||||
@Throws(UpdateStatusException::class)
|
||||
|
@ -151,8 +152,8 @@ class UpdateStatusTask(
|
|||
|
||||
@Throws(UploadException::class)
|
||||
private fun uploadMedia(uploader: MediaUploaderInterface?,
|
||||
update: ParcelableStatusUpdate,
|
||||
pendingUpdate: PendingStatusUpdate) {
|
||||
update: ParcelableStatusUpdate,
|
||||
pendingUpdate: PendingStatusUpdate) {
|
||||
stateCallback.onStartUploadingMedia()
|
||||
if (uploader == null) {
|
||||
uploadMediaWithDefaultProvider(update, pendingUpdate)
|
||||
|
@ -163,8 +164,8 @@ class UpdateStatusTask(
|
|||
|
||||
@Throws(UploadException::class)
|
||||
private fun uploadMediaWithExtension(uploader: MediaUploaderInterface,
|
||||
update: ParcelableStatusUpdate,
|
||||
pending: PendingStatusUpdate) {
|
||||
update: ParcelableStatusUpdate,
|
||||
pending: PendingStatusUpdate) {
|
||||
uploader.waitForService()
|
||||
val media: Array<UploaderMediaItem>
|
||||
try {
|
||||
|
@ -201,8 +202,8 @@ class UpdateStatusTask(
|
|||
|
||||
@Throws(UpdateStatusException::class)
|
||||
private fun shortenStatus(shortener: StatusShortenerInterface?,
|
||||
update: ParcelableStatusUpdate,
|
||||
pending: PendingStatusUpdate) {
|
||||
update: ParcelableStatusUpdate,
|
||||
pending: PendingStatusUpdate) {
|
||||
if (shortener == null) return
|
||||
stateCallback.onShorteningStatus()
|
||||
val sharedShortened = HashMap<UserKey, StatusShortenResult>()
|
||||
|
@ -280,8 +281,8 @@ class UpdateStatusTask(
|
|||
|
||||
@Throws(MicroBlogException::class, UploadException::class)
|
||||
private fun fanfouUpdateStatusWithPhoto(microBlog: MicroBlog, statusUpdate: ParcelableStatusUpdate,
|
||||
pendingUpdate: PendingStatusUpdate, overrideText: String,
|
||||
sizeLimit: SizeLimit, updateIndex: Int): Status {
|
||||
pendingUpdate: PendingStatusUpdate, overrideText: String,
|
||||
sizeLimit: SizeLimit, updateIndex: Int): Status {
|
||||
if (statusUpdate.media.size > 1) {
|
||||
throw MicroBlogException(context.getString(R.string.error_too_many_photos_fanfou))
|
||||
}
|
||||
|
@ -353,8 +354,8 @@ class UpdateStatusTask(
|
|||
|
||||
@Throws(MicroBlogException::class)
|
||||
private fun twitterUpdateStatus(microBlog: MicroBlog, statusUpdate: ParcelableStatusUpdate,
|
||||
pendingUpdate: PendingStatusUpdate, overrideText: String,
|
||||
index: Int): Status {
|
||||
pendingUpdate: PendingStatusUpdate, overrideText: String,
|
||||
index: Int): Status {
|
||||
val status = StatusUpdate(overrideText)
|
||||
if (statusUpdate.in_reply_to_status != null) {
|
||||
status.inReplyToStatusId(statusUpdate.in_reply_to_status.id)
|
||||
|
@ -500,7 +501,7 @@ class UpdateStatusTask(
|
|||
|
||||
@Throws(IOException::class, MicroBlogException::class)
|
||||
private fun uploadMediaChucked(upload: TwitterUpload, body: Body,
|
||||
ownerIds: Array<String>): MediaUploadResponse {
|
||||
ownerIds: Array<String>): MediaUploadResponse {
|
||||
val mediaType = body.contentType().contentType
|
||||
val length = body.length()
|
||||
val stream = body.stream()
|
||||
|
@ -550,11 +551,10 @@ class UpdateStatusTask(
|
|||
}
|
||||
}
|
||||
|
||||
private fun saveDraft(@Draft.Action draftAction: String?, statusUpdate: ParcelableStatusUpdate): Long {
|
||||
return saveDraft(context, draftAction) {
|
||||
private fun saveDraft(statusUpdate: ParcelableStatusUpdate): Long {
|
||||
return saveDraft(context, statusUpdate.draft_action ?: Draft.Action.UPDATE_STATUS) {
|
||||
this.unique_id = statusUpdate.draft_unique_id ?: UUID.randomUUID().toString()
|
||||
this.account_keys = statusUpdate.accounts.map { it.key }.toTypedArray()
|
||||
this.action_type = draftAction ?: Draft.Action.UPDATE_STATUS
|
||||
this.text = statusUpdate.text
|
||||
this.location = statusUpdate.location
|
||||
this.media = statusUpdate.media
|
||||
|
@ -907,7 +907,7 @@ class UpdateStatusTask(
|
|||
val deleteAlways: List<MediaDeletionItem>?
|
||||
)
|
||||
|
||||
fun saveDraft(context: Context, @Draft.Action action: String?, config: Draft.() -> Unit): Long {
|
||||
fun saveDraft(context: Context, @Draft.Action action: String, config: Draft.() -> Unit): Long {
|
||||
val draft = Draft()
|
||||
draft.action_type = action
|
||||
draft.timestamp = System.currentTimeMillis()
|
||||
|
|
|
@ -649,55 +649,6 @@ class AsyncTwitterWrapper(
|
|||
|
||||
}
|
||||
|
||||
|
||||
internal inner class DestroyMessageConversationTask(private val mAccountKey: UserKey, private val mUserId: String) : ManagedAsyncTask<Any, Any, SingleResponse<Boolean>>(context) {
|
||||
|
||||
private fun deleteMessages(accountKey: UserKey, userId: String) {
|
||||
val whereArgs = arrayOf(accountKey.toString(), userId)
|
||||
resolver.delete(DirectMessages.Inbox.CONTENT_URI, Expression.and(
|
||||
Expression.equalsArgs(AccountSupportColumns.ACCOUNT_KEY),
|
||||
Expression.equalsArgs(Inbox.SENDER_ID)
|
||||
).sql, whereArgs)
|
||||
resolver.delete(DirectMessages.Outbox.CONTENT_URI, Expression.and(
|
||||
Expression.equalsArgs(AccountSupportColumns.ACCOUNT_KEY),
|
||||
Expression.equalsArgs(Outbox.RECIPIENT_ID)
|
||||
).sql, whereArgs)
|
||||
}
|
||||
|
||||
private fun isMessageNotFound(e: Exception?): Boolean {
|
||||
if (e !is MicroBlogException) return false
|
||||
return e.errorCode == ErrorInfo.PAGE_NOT_FOUND || e.statusCode == HttpResponseCode.NOT_FOUND
|
||||
}
|
||||
|
||||
override fun doInBackground(vararg args: Any): SingleResponse<Boolean> {
|
||||
val microBlog = MicroBlogAPIFactory.getInstance(context, mAccountKey) ?: return SingleResponse(MicroBlogException("No account"))
|
||||
try {
|
||||
microBlog.destroyDirectMessagesConversation(mAccountKey.id, mUserId)
|
||||
deleteMessages(mAccountKey, mUserId)
|
||||
return SingleResponse(true)
|
||||
} catch (e: MicroBlogException) {
|
||||
if (isMessageNotFound(e)) {
|
||||
deleteMessages(mAccountKey, mUserId)
|
||||
}
|
||||
return SingleResponse(e)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
override fun onPostExecute(result: SingleResponse<Boolean>) {
|
||||
super.onPostExecute(result)
|
||||
if (result.hasData() || isMessageNotFound(result.exception)) {
|
||||
Utils.showInfoMessage(context, R.string.message_direct_message_deleted, false)
|
||||
} else {
|
||||
Utils.showErrorMessage(context, R.string.action_deleting, result.exception, true)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
internal inner class DestroySavedSearchTask(
|
||||
context: Context,
|
||||
private val accountKey: UserKey,
|
||||
|
|
|
@ -20,18 +20,19 @@
|
|||
package org.mariotaku.twidere.util
|
||||
|
||||
import android.content.Context
|
||||
import android.content.SharedPreferences
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import android.support.v4.app.FragmentActivity
|
||||
import org.mariotaku.twidere.TwidereConstants.SHARED_PREFERENCES_NAME
|
||||
import org.mariotaku.kpreferences.get
|
||||
import org.mariotaku.twidere.constant.IntentConstants.EXTRA_URI
|
||||
import org.mariotaku.twidere.constant.SharedPreferenceConstants.KEY_PHISHING_LINK_WARNING
|
||||
import org.mariotaku.twidere.constant.phishingLinksWaringKey
|
||||
import org.mariotaku.twidere.fragment.PhishingLinkWarningDialogFragment
|
||||
|
||||
class DirectMessageOnLinkClickHandler(
|
||||
context: Context,
|
||||
manager: MultiSelectManager?,
|
||||
preferences: SharedPreferencesWrapper
|
||||
preferences: SharedPreferences
|
||||
) : OnLinkClickHandler(context, manager, preferences) {
|
||||
|
||||
override val isPrivateData: Boolean
|
||||
|
@ -43,8 +44,7 @@ class DirectMessageOnLinkClickHandler(
|
|||
super.openLink(link)
|
||||
return
|
||||
}
|
||||
val prefs = context.getSharedPreferences(SHARED_PREFERENCES_NAME, Context.MODE_PRIVATE)
|
||||
if (context is FragmentActivity && prefs.getBoolean(KEY_PHISHING_LINK_WARNING, true)) {
|
||||
if (context is FragmentActivity && preferences[phishingLinksWaringKey]) {
|
||||
val fm = context.supportFragmentManager
|
||||
val fragment = PhishingLinkWarningDialogFragment()
|
||||
val args = Bundle()
|
||||
|
|
|
@ -62,11 +62,10 @@
|
|||
app:caretWidth="@dimen/element_spacing_normal"
|
||||
app:cornerRadius="2dp">
|
||||
|
||||
<LinearLayout
|
||||
<RelativeLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_below="@+id/mediaPreview"
|
||||
android:orientation="vertical"
|
||||
android:padding="@dimen/element_spacing_normal">
|
||||
|
||||
<org.mariotaku.twidere.view.CardMediaContainer
|
||||
|
@ -80,16 +79,16 @@
|
|||
|
||||
</org.mariotaku.twidere.view.CardMediaContainer>
|
||||
|
||||
|
||||
<org.mariotaku.twidere.view.TimelineContentTextView
|
||||
android:id="@+id/text"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_below="@+id/mediaPreview"
|
||||
android:textAppearance="?android:attr/textAppearanceSmall"
|
||||
android:textColor="?android:attr/textColorPrimary"
|
||||
tools:text="@string/sample_status_text"/>
|
||||
|
||||
</LinearLayout>
|
||||
</RelativeLayout>
|
||||
</org.mariotaku.messagebubbleview.library.MessageBubbleView>
|
||||
|
||||
<org.mariotaku.twidere.view.FixedTextView
|
||||
|
|
Loading…
Reference in New Issue