mirror of
https://github.com/TwidereProject/Twidere-Android
synced 2025-01-31 08:54:57 +01:00
supports conversation load more
This commit is contained in:
parent
af1c45b4d7
commit
42db9325c8
@ -38,13 +38,18 @@ import org.mariotaku.restfu.annotation.param.Queries;
|
||||
import org.mariotaku.restfu.annotation.param.Query;
|
||||
import org.mariotaku.restfu.http.BodyType;
|
||||
|
||||
|
||||
@Queries(@KeyValue(key = "include_groups", value = "true"))
|
||||
@Queries({@KeyValue(key = "include_groups", value = "true"),
|
||||
@KeyValue(key = "include_conversation_info", value = "true"),
|
||||
@KeyValue(key = "ext", value = "stickerInfo,mediaRestrictions,altText")})
|
||||
public interface PrivateDirectMessagesResources extends PrivateResources {
|
||||
|
||||
@POST("/dm/conversation/{conversation_id}/delete.json")
|
||||
@BodyType(BodyType.FORM)
|
||||
ResponseCode destroyDirectMessagesConversation(@Path("conversation_id") String conversationId) throws MicroBlogException;
|
||||
ResponseCode deleteDmConversation(@Path("conversation_id") String conversationId) throws MicroBlogException;
|
||||
|
||||
@POST("/dm/conversation/{conversation_id}/update_name.json")
|
||||
@BodyType(BodyType.FORM)
|
||||
ResponseCode updateDmConversationName(@Path("conversation_id") String conversationId, @Param("name") String name) throws MicroBlogException;
|
||||
|
||||
@POST("/dm/new.json")
|
||||
DMResponse sendDm(@Param NewDm newDm) throws MicroBlogException;
|
||||
@ -56,5 +61,5 @@ public interface PrivateDirectMessagesResources extends PrivateResources {
|
||||
UserEvents getUserUpdates(@Query("cursor") String cursor) throws MicroBlogException;
|
||||
|
||||
@GET("/dm/conversation/{conversation_id}.json")
|
||||
ConversationTimeline getUserInbox(@Path("conversation_id") String conversationId, @Query Paging paging) throws MicroBlogException;
|
||||
ConversationTimeline getDmConversation(@Path("conversation_id") String conversationId, @Query Paging paging) throws MicroBlogException;
|
||||
}
|
||||
|
@ -21,6 +21,7 @@ package org.mariotaku.twidere.model.event;
|
||||
|
||||
import android.net.Uri;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* Created by mariotaku on 14/12/10.
|
||||
@ -29,11 +30,14 @@ public class GetMessagesTaskEvent {
|
||||
|
||||
@NonNull
|
||||
public final Uri uri;
|
||||
@Nullable
|
||||
public final String taskTag;
|
||||
public final boolean running;
|
||||
public final Exception exception;
|
||||
|
||||
public GetMessagesTaskEvent(@NonNull Uri uri, boolean running, Exception exception) {
|
||||
public GetMessagesTaskEvent(@NonNull Uri uri, @Nullable String taskTag, boolean running, Exception exception) {
|
||||
this.uri = uri;
|
||||
this.taskTag = taskTag;
|
||||
this.running = running;
|
||||
this.exception = exception;
|
||||
}
|
||||
|
@ -195,17 +195,18 @@ public class MicroBlogAPIFactory implements TwidereConstants {
|
||||
public static String getUserAgentName(Context context, ConsumerKeyType type) {
|
||||
switch (type) {
|
||||
case TWITTER_FOR_ANDROID: {
|
||||
final String versionName = "5.2.4";
|
||||
final String internalVersionName = "524-r1";
|
||||
final String versionName = "6.3.4";
|
||||
final String internalVersionName = "6110049-r-917";
|
||||
final String model = Build.MODEL;
|
||||
final String manufacturer = Build.MANUFACTURER;
|
||||
final int sdkInt = Build.VERSION.SDK_INT;
|
||||
final String sdkRelease = Build.VERSION.RELEASE;
|
||||
final String device = Build.DEVICE;
|
||||
final String brand = Build.BRAND;
|
||||
final String product = Build.PRODUCT;
|
||||
final int debug = BuildConfig.DEBUG ? 1 : 0;
|
||||
return String.format(Locale.ROOT, "TwitterAndroid /%s (%s) %s/%d (%s;%s;%s;%s;%d)",
|
||||
versionName, internalVersionName, model, sdkInt, manufacturer, device, brand,
|
||||
return String.format(Locale.US, "TwitterAndroid /%s (%s) %s/%s (%s;%s;%s;%s;%d)",
|
||||
versionName, internalVersionName, model, sdkRelease, manufacturer, model, brand,
|
||||
product, debug);
|
||||
}
|
||||
case TWITTER_FOR_IPHONE: {
|
||||
|
@ -27,6 +27,7 @@ import android.view.ViewGroup
|
||||
import org.apache.commons.lang3.time.DateUtils
|
||||
import org.mariotaku.kpreferences.get
|
||||
import org.mariotaku.twidere.adapter.iface.IItemCountsAdapter
|
||||
import org.mariotaku.twidere.adapter.iface.ILoadMoreSupportAdapter
|
||||
import org.mariotaku.twidere.annotation.PreviewStyle
|
||||
import org.mariotaku.twidere.constant.linkHighlightOptionKey
|
||||
import org.mariotaku.twidere.constant.mediaPreviewStyleKey
|
||||
@ -38,6 +39,7 @@ import org.mariotaku.twidere.util.DirectMessageOnLinkClickHandler
|
||||
import org.mariotaku.twidere.util.MediaLoadingHandler
|
||||
import org.mariotaku.twidere.util.TwidereLinkify
|
||||
import org.mariotaku.twidere.view.CardMediaContainer.OnMediaClickListener
|
||||
import org.mariotaku.twidere.view.holder.LoadIndicatorViewHolder
|
||||
import org.mariotaku.twidere.view.holder.message.AbsMessageViewHolder
|
||||
import org.mariotaku.twidere.view.holder.message.MessageViewHolder
|
||||
import org.mariotaku.twidere.view.holder.message.NoticeSummaryEventViewHolder
|
||||
@ -47,7 +49,7 @@ import java.util.*
|
||||
class MessagesConversationAdapter(context: Context) : LoadMoreSupportAdapter<RecyclerView.ViewHolder>(context),
|
||||
IItemCountsAdapter {
|
||||
private val calendars = Pair(Calendar.getInstance(), Calendar.getInstance())
|
||||
override val itemCounts: ItemCounts = ItemCounts(1)
|
||||
override val itemCounts: ItemCounts = ItemCounts(2)
|
||||
|
||||
@PreviewStyle
|
||||
val mediaPreviewStyle: Int = preferences[mediaPreviewStyleKey]
|
||||
@ -60,13 +62,17 @@ class MessagesConversationAdapter(context: Context) : LoadMoreSupportAdapter<Rec
|
||||
listener?.onMediaClick(id.toInt(), media, accountKey)
|
||||
}
|
||||
}
|
||||
val messageRange: IntRange
|
||||
get() {
|
||||
return itemCounts.getItemStartPosition(ITEM_START_MESSAGE) until itemCounts[ITEM_START_MESSAGE]
|
||||
}
|
||||
|
||||
var messages: List<ParcelableMessage>? = null
|
||||
private set
|
||||
var conversation: ParcelableMessageConversation? = null
|
||||
private set
|
||||
var listener: Listener? = null
|
||||
|
||||
var displaySenderProfile: Boolean = false
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
|
||||
val inflater = LayoutInflater.from(parent.context)
|
||||
@ -89,6 +95,11 @@ class MessagesConversationAdapter(context: Context) : LoadMoreSupportAdapter<Rec
|
||||
holder.setup()
|
||||
return holder
|
||||
}
|
||||
ITEM_LOAD_OLDER_INDICATOR -> {
|
||||
val view = inflater.inflate(LoadIndicatorViewHolder.layoutResource, parent, false)
|
||||
val holder = LoadIndicatorViewHolder(view)
|
||||
return holder
|
||||
}
|
||||
}
|
||||
throw UnsupportedOperationException()
|
||||
}
|
||||
@ -114,6 +125,7 @@ class MessagesConversationAdapter(context: Context) : LoadMoreSupportAdapter<Rec
|
||||
|
||||
override fun getItemCount(): Int {
|
||||
itemCounts[ITEM_START_MESSAGE] = messages?.size ?: 0
|
||||
itemCounts[ITEM_START_LOAD_OLDER] = if (loadMoreIndicatorPosition and ILoadMoreSupportAdapter.START != 0L) 1 else 0
|
||||
return itemCounts.itemCount
|
||||
}
|
||||
|
||||
@ -132,12 +144,13 @@ class MessagesConversationAdapter(context: Context) : LoadMoreSupportAdapter<Rec
|
||||
else -> return ITEM_TYPE_TEXT_MESSAGE
|
||||
}
|
||||
}
|
||||
ITEM_START_LOAD_OLDER -> return ITEM_LOAD_OLDER_INDICATOR
|
||||
}
|
||||
throw UnsupportedOperationException()
|
||||
}
|
||||
|
||||
fun getMessage(position: Int): ParcelableMessage? {
|
||||
return messages?.get(position - itemCounts.getItemStartPosition(0))
|
||||
return messages?.get(position - itemCounts.getItemStartPosition(ITEM_START_MESSAGE))
|
||||
}
|
||||
|
||||
fun findUser(key: UserKey): ParcelableUser? {
|
||||
@ -156,10 +169,12 @@ class MessagesConversationAdapter(context: Context) : LoadMoreSupportAdapter<Rec
|
||||
|
||||
companion object {
|
||||
private const val ITEM_START_MESSAGE = 0
|
||||
private const val ITEM_START_LOAD_OLDER = 1
|
||||
|
||||
const val ITEM_TYPE_TEXT_MESSAGE = 1
|
||||
const val ITEM_TYPE_STICKER_MESSAGE = 2
|
||||
const val ITEM_TYPE_NOTICE_MESSAGE = 3
|
||||
private const val ITEM_TYPE_TEXT_MESSAGE = 1
|
||||
private const val ITEM_TYPE_STICKER_MESSAGE = 2
|
||||
private const val ITEM_TYPE_NOTICE_MESSAGE = 3
|
||||
private const val ITEM_LOAD_OLDER_INDICATOR = 4
|
||||
}
|
||||
|
||||
|
||||
|
@ -10,9 +10,11 @@ import android.support.v4.app.LoaderManager
|
||||
import android.support.v4.content.Loader
|
||||
import android.support.v7.widget.FixedLinearLayoutManager
|
||||
import android.support.v7.widget.LinearLayoutManager
|
||||
import android.support.v7.widget.RecyclerView
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import com.squareup.otto.Subscribe
|
||||
import kotlinx.android.synthetic.main.fragment_messages_conversation.*
|
||||
import org.mariotaku.abstask.library.TaskStarter
|
||||
import org.mariotaku.kpreferences.get
|
||||
@ -25,22 +27,26 @@ import org.mariotaku.twidere.TwidereConstants.REQUEST_PICK_MEDIA
|
||||
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.constant.IntentConstants.EXTRA_ACCOUNT_KEY
|
||||
import org.mariotaku.twidere.constant.IntentConstants.EXTRA_CONVERSATION_ID
|
||||
import org.mariotaku.twidere.constant.newDocumentApiKey
|
||||
import org.mariotaku.twidere.loader.ObjectCursorLoader
|
||||
import org.mariotaku.twidere.model.*
|
||||
import org.mariotaku.twidere.model.ParcelableMessageConversation.ConversationType
|
||||
import org.mariotaku.twidere.model.event.GetMessagesTaskEvent
|
||||
import org.mariotaku.twidere.model.util.AccountUtils
|
||||
import org.mariotaku.twidere.provider.TwidereDataStore.Messages
|
||||
import org.mariotaku.twidere.service.LengthyOperationsService
|
||||
import org.mariotaku.twidere.task.GetMessagesTask
|
||||
import org.mariotaku.twidere.task.compose.AbsAddMediaTask
|
||||
import org.mariotaku.twidere.util.DataStoreUtils
|
||||
import org.mariotaku.twidere.util.IntentUtils
|
||||
import org.mariotaku.twidere.util.PreviewGridItemDecoration
|
||||
import java.util.concurrent.atomic.AtomicReference
|
||||
|
||||
class MessagesConversationFragment : BaseFragment(), LoaderManager.LoaderCallbacks<List<ParcelableMessage>?> {
|
||||
private lateinit var conversationAdapter: MessagesConversationAdapter
|
||||
class MessagesConversationFragment : AbsContentListRecyclerViewFragment<MessagesConversationAdapter>(),
|
||||
LoaderManager.LoaderCallbacks<List<ParcelableMessage>?> {
|
||||
private lateinit var mediaPreviewAdapter: MediaPreviewAdapter
|
||||
|
||||
private val accountKey: UserKey get() = arguments.getParcelable(EXTRA_ACCOUNT_KEY)
|
||||
@ -51,12 +57,22 @@ class MessagesConversationFragment : BaseFragment(), LoaderManager.LoaderCallbac
|
||||
AccountUtils.getAccountDetails(AccountManager.get(context), accountKey, true)
|
||||
}
|
||||
|
||||
private val loadMoreTaskTag: String
|
||||
get() = "loadMore:$accountKey:$conversationId"
|
||||
|
||||
// Layout manager reversed, so treat start as end
|
||||
override val reachingEnd: Boolean
|
||||
get() = super.reachingStart
|
||||
|
||||
// Layout manager reversed, so treat end as start
|
||||
override val reachingStart: Boolean
|
||||
get() = super.reachingEnd
|
||||
|
||||
override fun onActivityCreated(savedInstanceState: Bundle?) {
|
||||
super.onActivityCreated(savedInstanceState)
|
||||
conversationAdapter = MessagesConversationAdapter(context)
|
||||
conversationAdapter.listener = object : MessagesConversationAdapter.Listener {
|
||||
adapter.listener = object : MessagesConversationAdapter.Listener {
|
||||
override fun onMediaClick(position: Int, media: ParcelableMedia, accountKey: UserKey?) {
|
||||
val message = conversationAdapter.getMessage(position) ?: return
|
||||
val message = adapter.getMessage(position) ?: return
|
||||
IntentUtils.openMediaDirectly(context = context, accountKey = accountKey,
|
||||
media = message.media, current = media,
|
||||
newDocument = preferences[newDocumentApiKey], message = message)
|
||||
@ -64,9 +80,6 @@ class MessagesConversationFragment : BaseFragment(), LoaderManager.LoaderCallbac
|
||||
}
|
||||
mediaPreviewAdapter = MediaPreviewAdapter(context)
|
||||
|
||||
recyclerView.adapter = conversationAdapter
|
||||
recyclerView.layoutManager = FixedLinearLayoutManager(context, LinearLayoutManager.VERTICAL, true)
|
||||
|
||||
attachedMediaPreview.layoutManager = FixedLinearLayoutManager(context, LinearLayoutManager.HORIZONTAL, false)
|
||||
attachedMediaPreview.adapter = mediaPreviewAdapter
|
||||
attachedMediaPreview.addItemDecoration(PreviewGridItemDecoration(resources.getDimensionPixelSize(R.dimen.element_spacing_small)))
|
||||
@ -78,9 +91,24 @@ class MessagesConversationFragment : BaseFragment(), LoaderManager.LoaderCallbac
|
||||
openMediaPicker()
|
||||
}
|
||||
|
||||
// No refresh for this fragment
|
||||
refreshEnabled = false
|
||||
adapter.loadMoreSupportedPosition = ILoadMoreSupportAdapter.NONE
|
||||
|
||||
updateMediaPreview()
|
||||
|
||||
loaderManager.initLoader(0, null, this)
|
||||
showProgress()
|
||||
}
|
||||
|
||||
override fun onStart() {
|
||||
super.onStart()
|
||||
bus.register(this)
|
||||
}
|
||||
|
||||
override fun onStop() {
|
||||
bus.unregister(this)
|
||||
super.onStop()
|
||||
}
|
||||
|
||||
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
||||
@ -103,18 +131,55 @@ class MessagesConversationFragment : BaseFragment(), LoaderManager.LoaderCallbac
|
||||
}
|
||||
|
||||
override fun onLoaderReset(loader: Loader<List<ParcelableMessage>?>) {
|
||||
conversationAdapter.setData(null, null)
|
||||
adapter.setData(null, null)
|
||||
}
|
||||
|
||||
override fun onLoadFinished(loader: Loader<List<ParcelableMessage>?>, data: List<ParcelableMessage>?) {
|
||||
val conversationLoader = loader as? ConversationLoader
|
||||
val conversation = conversationLoader?.conversation
|
||||
conversationAdapter.setData(conversation, data)
|
||||
adapter.setData(conversation, data)
|
||||
adapter.displaySenderProfile = conversation?.conversation_type == ConversationType.GROUP
|
||||
if (conversation?.conversation_extras_type == ParcelableMessageConversation.ExtrasType.TWITTER_OFFICIAL) {
|
||||
adapter.loadMoreSupportedPosition = ILoadMoreSupportAdapter.START
|
||||
} else {
|
||||
adapter.loadMoreSupportedPosition = ILoadMoreSupportAdapter.NONE
|
||||
}
|
||||
showContent()
|
||||
}
|
||||
|
||||
override fun onCreateAdapter(context: Context): MessagesConversationAdapter {
|
||||
return MessagesConversationAdapter(context)
|
||||
}
|
||||
|
||||
override fun onCreateLayoutManager(context: Context): LinearLayoutManager {
|
||||
return FixedLinearLayoutManager(context, LinearLayoutManager.VERTICAL, true)
|
||||
}
|
||||
|
||||
override fun createItemDecoration(context: Context, recyclerView: RecyclerView,
|
||||
layoutManager: LinearLayoutManager): RecyclerView.ItemDecoration? {
|
||||
return null
|
||||
}
|
||||
|
||||
override fun onLoadMoreContents(position: Long) {
|
||||
if (position and ILoadMoreSupportAdapter.START == 0L) return
|
||||
val message = adapter.getMessage(adapter.messageRange.endInclusive) ?: return
|
||||
setLoadMoreIndicatorPosition(position)
|
||||
val param = GetMessagesTask.LoadMoreMessageTaskParam(context, accountKey, conversationId,
|
||||
message.id)
|
||||
param.taskTag = loadMoreTaskTag
|
||||
twitterWrapper.getMessagesAsync(param)
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
fun onGetMessagesTaskEvent(event: GetMessagesTaskEvent) {
|
||||
if (!event.running && event.taskTag == loadMoreTaskTag) {
|
||||
setLoadMoreIndicatorPosition(ILoadMoreSupportAdapter.NONE)
|
||||
}
|
||||
}
|
||||
|
||||
private fun performSendMessage() {
|
||||
val conversation = conversationAdapter.conversation ?: return
|
||||
if (editText.empty && conversationAdapter.itemCount == 0) {
|
||||
val conversation = adapter.conversation ?: return
|
||||
if (editText.empty && adapter.itemCount == 0) {
|
||||
editText.error = getString(R.string.hint_error_message_no_content)
|
||||
return
|
||||
}
|
||||
|
@ -82,8 +82,8 @@ class MessagesEntriesFragment : AbsContentListRecyclerViewFragment<MessagesEntri
|
||||
|
||||
override fun triggerRefresh(): Boolean {
|
||||
super.triggerRefresh()
|
||||
twitterWrapper.getMessagesAsync(GetMessagesTask.RefreshNewTaskParam(context) {
|
||||
this@MessagesEntriesFragment.accountKeys
|
||||
twitterWrapper.getMessagesAsync(object : GetMessagesTask.RefreshNewTaskParam(context) {
|
||||
override val accountKeys: Array<UserKey> = this@MessagesEntriesFragment.accountKeys
|
||||
})
|
||||
return true
|
||||
}
|
||||
@ -93,8 +93,8 @@ class MessagesEntriesFragment : AbsContentListRecyclerViewFragment<MessagesEntri
|
||||
return
|
||||
}
|
||||
setLoadMoreIndicatorPosition(ILoadMoreSupportAdapter.END)
|
||||
twitterWrapper.getMessagesAsync(GetMessagesTask.LoadMoreTaskParam(context) {
|
||||
this@MessagesEntriesFragment.accountKeys
|
||||
twitterWrapper.getMessagesAsync(object : GetMessagesTask.LoadMoreEntriesTaskParam(context) {
|
||||
override val accountKeys: Array<UserKey> = this@MessagesEntriesFragment.accountKeys
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -62,7 +62,7 @@ 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.message.SendMessageTask
|
||||
import org.mariotaku.twidere.task.twitter.UpdateStatusTask
|
||||
import org.mariotaku.twidere.util.NotificationManagerWrapper
|
||||
import org.mariotaku.twidere.util.Utils
|
||||
|
@ -56,7 +56,7 @@ class GetMessagesTask(
|
||||
|
||||
override fun afterExecute(callback: ((Boolean) -> Unit)?, result: Unit) {
|
||||
callback?.invoke(true)
|
||||
bus.post(GetMessagesTaskEvent(Messages.CONTENT_URI, false, null))
|
||||
bus.post(GetMessagesTaskEvent(Messages.CONTENT_URI, params?.taskTag, false, null))
|
||||
}
|
||||
|
||||
private fun getMessages(microBlog: MicroBlog, details: AccountDetails, param: RefreshMessagesTaskParam, index: Int): DatabaseUpdateData {
|
||||
@ -82,7 +82,7 @@ class GetMessagesTask(
|
||||
if (conversationId == null) {
|
||||
return getTwitterOfficialUserInbox(microBlog, details, param, index)
|
||||
} else {
|
||||
return DatabaseUpdateData(emptyList(), emptyList())
|
||||
return getTwitterOfficialConversation(microBlog, details, conversationId, param, index)
|
||||
}
|
||||
}
|
||||
|
||||
@ -155,11 +155,18 @@ class GetMessagesTask(
|
||||
}
|
||||
|
||||
private fun getTwitterOfficialConversation(microBlog: MicroBlog, details: AccountDetails,
|
||||
conversationId: String, param: RefreshMessagesTaskParam, index: Int) {
|
||||
conversationId: String, param: RefreshMessagesTaskParam, index: Int): DatabaseUpdateData {
|
||||
val maxId = param.maxIds?.get(index) ?: return DatabaseUpdateData(emptyList(), emptyList())
|
||||
val paging = Paging().apply {
|
||||
maxId(maxId)
|
||||
}
|
||||
|
||||
val response = microBlog.getDmConversation(conversationId, paging).conversationTimeline
|
||||
return createDatabaseUpdateData(context, details, response)
|
||||
}
|
||||
|
||||
private fun getTwitterOfficialUserInbox(microBlog: MicroBlog, details: AccountDetails, param: RefreshMessagesTaskParam, index: Int): DatabaseUpdateData {
|
||||
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) {
|
||||
@ -211,10 +218,9 @@ class GetMessagesTask(
|
||||
val conversationRequestCursor: String? = null
|
||||
)
|
||||
|
||||
class RefreshNewTaskParam(
|
||||
context: Context,
|
||||
getAccountKeys: () -> Array<UserKey>
|
||||
) : RefreshMessagesTaskParam(context, getAccountKeys) {
|
||||
abstract class RefreshNewTaskParam(
|
||||
context: Context
|
||||
) : RefreshMessagesTaskParam(context) {
|
||||
|
||||
override val sinceIds: Array<String?>?
|
||||
get() {
|
||||
@ -241,10 +247,9 @@ class GetMessagesTask(
|
||||
override val hasCursors: Boolean = true
|
||||
}
|
||||
|
||||
class LoadMoreTaskParam(
|
||||
context: Context,
|
||||
getAccountKeys: () -> Array<UserKey>
|
||||
) : RefreshMessagesTaskParam(context, getAccountKeys) {
|
||||
abstract class LoadMoreEntriesTaskParam(
|
||||
context: Context
|
||||
) : RefreshMessagesTaskParam(context) {
|
||||
|
||||
override val maxIds: Array<String?>? by lazy {
|
||||
val incomingIds = DataStoreUtils.getOldestMessageIds(context, Messages.CONTENT_URI,
|
||||
@ -264,15 +269,27 @@ class GetMessagesTask(
|
||||
override val hasMaxIds: Boolean = true
|
||||
}
|
||||
|
||||
open class RefreshMessagesTaskParam(
|
||||
val context: Context,
|
||||
val getAccountKeys: () -> Array<UserKey>
|
||||
class LoadMoreMessageTaskParam(
|
||||
context: Context,
|
||||
accountKey: UserKey,
|
||||
override val conversationId: String,
|
||||
maxId: String
|
||||
) : RefreshMessagesTaskParam(context) {
|
||||
override val accountKeys: Array<UserKey> = arrayOf(accountKey)
|
||||
override val maxIds: Array<String?>? = arrayOf(maxId)
|
||||
override val hasMaxIds: Boolean = true
|
||||
}
|
||||
|
||||
abstract class RefreshMessagesTaskParam(
|
||||
val context: Context
|
||||
) : SimpleRefreshTaskParam() {
|
||||
|
||||
/**
|
||||
* If `conversationId` has value, load messages in conversationId
|
||||
*/
|
||||
open var conversationId: String? = null
|
||||
open val conversationId: String? = null
|
||||
|
||||
var taskTag: String? = null
|
||||
|
||||
protected val accounts: Array<AccountDetails?> by lazy {
|
||||
AccountUtils.getAllAccountDetails(AccountManager.get(context), accountKeys, false)
|
||||
@ -298,15 +315,12 @@ class GetMessagesTask(
|
||||
}.toTypedArray()
|
||||
}
|
||||
|
||||
override final val accountKeys: Array<UserKey>
|
||||
get() = getAccountKeys()
|
||||
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
fun createDatabaseUpdateData(context: Context, account: AccountDetails,
|
||||
response: DMResponse): DatabaseUpdateData {
|
||||
fun createDatabaseUpdateData(context: Context, account: AccountDetails, response: DMResponse):
|
||||
DatabaseUpdateData {
|
||||
val respConversations = response.conversations.orEmpty()
|
||||
val respEntries = response.entries.orEmpty()
|
||||
val respUsers = response.users.orEmpty()
|
||||
|
@ -0,0 +1,40 @@
|
||||
/*
|
||||
* 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.task.message
|
||||
|
||||
import android.content.Context
|
||||
import org.mariotaku.microblog.library.MicroBlogException
|
||||
import org.mariotaku.twidere.model.UserKey
|
||||
import org.mariotaku.twidere.task.ExceptionHandlingAbstractTask
|
||||
|
||||
/**
|
||||
* Created by mariotaku on 2017/2/14.
|
||||
*/
|
||||
|
||||
class DestroyConversationTask(
|
||||
context: Context,
|
||||
val accountKey: UserKey,
|
||||
val conversationId: String
|
||||
) : ExceptionHandlingAbstractTask<Unit?, Unit, MicroBlogException, Unit?>(context) {
|
||||
override fun onExecute(params: Unit?) {
|
||||
throw UnsupportedOperationException("not implemented") //To change body of created functions use File | Settings | File Templates.
|
||||
}
|
||||
|
||||
}
|
@ -1,4 +1,23 @@
|
||||
package org.mariotaku.twidere.task
|
||||
/*
|
||||
* 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.task.message
|
||||
|
||||
import android.content.Context
|
||||
import org.mariotaku.ktextension.isNotNullOrEmpty
|
||||
@ -14,6 +33,8 @@ 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.ExceptionHandlingAbstractTask
|
||||
import org.mariotaku.twidere.task.GetMessagesTask
|
||||
import org.mariotaku.twidere.task.GetMessagesTask.Companion.addConversation
|
||||
import org.mariotaku.twidere.task.GetMessagesTask.Companion.addLocalConversations
|
||||
import org.mariotaku.twidere.task.twitter.UpdateStatusTask
|
@ -44,8 +44,6 @@ import org.mariotaku.twidere.model.*
|
||||
import org.mariotaku.twidere.model.event.*
|
||||
import org.mariotaku.twidere.model.util.ParcelableUserListUtils
|
||||
import org.mariotaku.twidere.provider.TwidereDataStore.*
|
||||
import org.mariotaku.twidere.provider.TwidereDataStore.DirectMessages.Inbox
|
||||
import org.mariotaku.twidere.provider.TwidereDataStore.DirectMessages.Outbox
|
||||
import org.mariotaku.twidere.task.*
|
||||
import org.mariotaku.twidere.util.collection.CompactHashSet
|
||||
import java.util.*
|
||||
@ -163,7 +161,7 @@ class AsyncTwitterWrapper(
|
||||
}
|
||||
|
||||
fun createUserListAsync(accountKey: UserKey, listName: String, isPublic: Boolean,
|
||||
description: String): Int {
|
||||
description: String): Int {
|
||||
val task = CreateUserListTask(context, accountKey, listName, isPublic,
|
||||
description)
|
||||
return asyncTaskManager.add(task, true)
|
||||
@ -293,7 +291,9 @@ class AsyncTwitterWrapper(
|
||||
})
|
||||
}
|
||||
if (preferences.getBoolean(SharedPreferenceConstants.KEY_HOME_REFRESH_DIRECT_MESSAGES)) {
|
||||
getMessagesAsync(GetMessagesTask.RefreshMessagesTaskParam(context, action))
|
||||
getMessagesAsync(object : GetMessagesTask.RefreshMessagesTaskParam(context) {
|
||||
override val accountKeys: Array<UserKey> by lazy { action() }
|
||||
})
|
||||
}
|
||||
if (preferences.getBoolean(SharedPreferenceConstants.KEY_HOME_REFRESH_SAVED_SEARCHES)) {
|
||||
getSavedSearchesAsync(action())
|
||||
@ -524,7 +524,7 @@ class AsyncTwitterWrapper(
|
||||
}
|
||||
|
||||
internal class CreateUserListTask(context: Context, private val mAccountKey: UserKey, private val mListName: String?,
|
||||
private val mIsPublic: Boolean, private val mDescription: String) : ManagedAsyncTask<Any, Any, SingleResponse<ParcelableUserList>>(context) {
|
||||
private val mIsPublic: Boolean, private val mDescription: String) : ManagedAsyncTask<Any, Any, SingleResponse<ParcelableUserList>>(context) {
|
||||
|
||||
override fun doInBackground(vararg params: Any): SingleResponse<ParcelableUserList> {
|
||||
val microBlog = MicroBlogAPIFactory.getInstance(context, mAccountKey
|
||||
|
@ -187,10 +187,11 @@ object DataStoreUtils {
|
||||
::ParcelableMessageConversationCursorIndices, { arrayOfNulls<ParcelableMessageConversation>(it) })
|
||||
}
|
||||
|
||||
fun getNewestConversations(context: Context, uri: Uri, accountKeys: Array<UserKey?>): Array<ParcelableMessageConversation?> {
|
||||
fun getNewestConversations(context: Context, uri: Uri, accountKeys: Array<UserKey?>,
|
||||
extraWhere: Expression? = null, extraWhereArgs: Array<String>? = null): Array<ParcelableMessageConversation?> {
|
||||
if (accountKeys.all { it == null }) return kotlin.arrayOfNulls(accountKeys.size)
|
||||
return getObjectFieldArray(context, uri, accountKeys, Conversations.ACCOUNT_KEY, Conversations.COLUMNS,
|
||||
OrderBy(SQLFunctions.MAX(Messages.LOCAL_TIMESTAMP)), null, null,
|
||||
OrderBy(SQLFunctions.MAX(Messages.LOCAL_TIMESTAMP)), extraWhere, extraWhereArgs,
|
||||
::ParcelableMessageConversationCursorIndices, { arrayOfNulls<ParcelableMessageConversation>(it) })
|
||||
}
|
||||
|
||||
@ -611,11 +612,11 @@ object DataStoreUtils {
|
||||
}
|
||||
|
||||
private fun <T> getObjectFieldArray(context: Context, uri: Uri, keys: Array<UserKey?>,
|
||||
keyField: String, valueFields: Array<String>, sortExpression: OrderBy?, extraHaving: Expression?,
|
||||
extraHavingArgs: Array<String>?, createIndices: (Cursor) -> ObjectCursor.CursorIndices<T>,
|
||||
keyField: String, valueFields: Array<String>, sortExpression: OrderBy?, extraWhere: Expression?,
|
||||
extraWhereArgs: Array<String>?, createIndices: (Cursor) -> ObjectCursor.CursorIndices<T>,
|
||||
createArray: (Int) -> Array<T?>): Array<T?> {
|
||||
return getFieldsArray(context, uri, keys, keyField, valueFields, sortExpression,
|
||||
extraHaving, extraHavingArgs, object : FieldArrayCreator<Array<T?>, ObjectCursor.CursorIndices<T>> {
|
||||
extraWhere, extraWhereArgs, object : FieldArrayCreator<Array<T?>, ObjectCursor.CursorIndices<T>> {
|
||||
override fun newArray(size: Int): Array<T?> {
|
||||
return createArray(size)
|
||||
}
|
||||
@ -631,10 +632,10 @@ object DataStoreUtils {
|
||||
}
|
||||
|
||||
private fun getStringFieldArray(context: Context, uri: Uri, keys: Array<UserKey?>,
|
||||
keyField: String, valueField: String, sortExpression: OrderBy?, extraHaving: Expression?,
|
||||
extraHavingArgs: Array<String>?): Array<String?> {
|
||||
keyField: String, valueField: String, sortExpression: OrderBy?, extraWhere: Expression?,
|
||||
extraWhereArgs: Array<String>?): Array<String?> {
|
||||
return getFieldsArray(context, uri, keys, keyField, arrayOf(valueField), sortExpression,
|
||||
extraHaving, extraHavingArgs, object : FieldArrayCreator<Array<String?>, Int> {
|
||||
extraWhere, extraWhereArgs, object : FieldArrayCreator<Array<String?>, Int> {
|
||||
override fun newArray(size: Int): Array<String?> {
|
||||
return arrayOfNulls(size)
|
||||
}
|
||||
|
@ -70,10 +70,12 @@ class TaskServiceRunner(
|
||||
}
|
||||
ACTION_REFRESH_DIRECT_MESSAGES -> {
|
||||
val task = GetMessagesTask(context)
|
||||
task.params = GetMessagesTask.RefreshNewTaskParam(context) {
|
||||
AccountPreferences.getAccountPreferences(context, DataStoreUtils.getAccountKeys(context)).filter {
|
||||
it.isAutoRefreshEnabled && it.isAutoRefreshDirectMessagesEnabled
|
||||
}.map(AccountPreferences::getAccountKey).toTypedArray()
|
||||
task.params = object : GetMessagesTask.RefreshNewTaskParam(context) {
|
||||
override val accountKeys: Array<UserKey> by lazy {
|
||||
AccountPreferences.getAccountPreferences(context, DataStoreUtils.getAccountKeys(context)).filter {
|
||||
it.isAutoRefreshEnabled && it.isAutoRefreshDirectMessagesEnabled
|
||||
}.map(AccountPreferences::getAccountKey).toTypedArray()
|
||||
}
|
||||
}
|
||||
return task
|
||||
}
|
||||
|
@ -70,7 +70,7 @@ abstract class AbsMessageViewHolder(itemView: View, val adapter: MessagesConvers
|
||||
val time = DateUtils.formatDateTime(context, message.timestamp, DateUtils.FORMAT_SHOW_TIME)
|
||||
if (message.is_outgoing) {
|
||||
this.text = time
|
||||
} else if (sender != null) {
|
||||
} else if (adapter.displaySenderProfile && sender != null) {
|
||||
val senderName = manager.getDisplayName(sender, adapter.nameFirst)
|
||||
this.text = context.getString(R.string.message_format_sender_time, senderName, time)
|
||||
} else {
|
||||
@ -79,7 +79,8 @@ abstract class AbsMessageViewHolder(itemView: View, val adapter: MessagesConvers
|
||||
}
|
||||
|
||||
profileImage?.apply {
|
||||
if (adapter.profileImageEnabled && sender != null && !message.is_outgoing) {
|
||||
if (adapter.displaySenderProfile && adapter.profileImageEnabled && sender != null
|
||||
&& !message.is_outgoing) {
|
||||
this.visibility = View.VISIBLE
|
||||
adapter.mediaLoader.displayProfileImage(this, sender)
|
||||
} else {
|
||||
|
@ -27,19 +27,12 @@
|
||||
android:visibility="visible"
|
||||
tools:context=".fragment.MessagesConversationFragment">
|
||||
|
||||
<FrameLayout
|
||||
<include
|
||||
android:id="@+id/listContainer"
|
||||
layout="@layout/fragment_content_recyclerview"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_above="@+id/inputPanel">
|
||||
|
||||
<org.mariotaku.twidere.view.ExtendedRecyclerView
|
||||
android:id="@+id/recyclerView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:scrollbars="vertical"/>
|
||||
|
||||
</FrameLayout>
|
||||
android:layout_above="@+id/inputPanel"/>
|
||||
|
||||
<View
|
||||
android:id="@+id/inputPanelShadowCompat"
|
||||
|
@ -72,7 +72,7 @@
|
||||
android:id="@+id/mediaPreview"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:minWidth="200dp"
|
||||
android:layout_marginBottom="@dimen/element_spacing_normal"
|
||||
android:visibility="gone">
|
||||
|
||||
<include layout="@layout/layout_card_media_preview"/>
|
||||
|
@ -1,9 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
<item
|
||||
android:id="@id/compose"
|
||||
android:icon="@drawable/ic_action_add"
|
||||
android:title="@string/new_direct_message"
|
||||
app:showAsAction="always"/>
|
||||
</menu>
|
@ -1,29 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?><!--
|
||||
~ Twidere - Twitter client for Android
|
||||
~
|
||||
~ Copyright (C) 2012-2014 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/>.
|
||||
-->
|
||||
|
||||
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
<item
|
||||
android:id="@id/delete_all"
|
||||
android:enabled="false"
|
||||
android:icon="@drawable/ic_action_delete"
|
||||
android:title="@string/delete_conversation"
|
||||
android:visible="false"
|
||||
app:showAsAction="never" />
|
||||
</menu>
|
@ -28,6 +28,7 @@
|
||||
<string name="action_creating_list">creating list</string>
|
||||
<!-- [verb] Action for deleting a file or a twitter object like tweet-->
|
||||
<string name="action_delete">Delete</string>
|
||||
<string name="action_delete_messages">Delete messages</string>
|
||||
<string name="action_deleting">deleting</string>
|
||||
<string name="action_deleting_search">deleting search</string>
|
||||
<string name="action_denying_follow_request">denying follow request</string>
|
||||
@ -52,6 +53,7 @@
|
||||
<string name="action_invert_selection">Invert selection</string>
|
||||
<!-- Used for decide something later, like permission request -->
|
||||
<string name="action_later">Later</string>
|
||||
<string name="action_leave_conversation">Leave conversation</string>
|
||||
<!-- [verb] e.g. An action label on a tweet to like this tweet. Formerly Twitter favorite. -->
|
||||
<string name="action_like">Like</string>
|
||||
<string name="action_liking">liking</string>
|
||||
@ -538,6 +540,7 @@
|
||||
<string name="hint_accounts_dashboard_message">Swipe from screen edge to open accounts dashboard.</string>
|
||||
<string name="hint_accounts_dashboard_title">Accounts dashboard</string>
|
||||
<string name="hint_empty_filters_subscriptions">No subscriptions</string>
|
||||
<string name="hint_error_message_no_content">No content</string>
|
||||
<string name="hint_no_account">No account</string>
|
||||
|
||||
<string name="hints">Hints</string>
|
||||
@ -812,7 +815,6 @@
|
||||
<string name="no_location">No location</string>
|
||||
<string name="no_rule">No rule</string>
|
||||
<string name="no_status_content_text">No content</string>
|
||||
<string name="hint_error_message_no_content">No content</string>
|
||||
<string name="no_tab">No tab</string>
|
||||
<string name="no_tab_hint">No tab</string>
|
||||
<string name="no_thanks">No, thanks</string>
|
||||
|
Loading…
x
Reference in New Issue
Block a user