supports conversation load more

This commit is contained in:
Mariotaku Lee 2017-02-15 00:26:48 +08:00
parent af1c45b4d7
commit 42db9325c8
No known key found for this signature in database
GPG Key ID: 15C10F89D7C33535
19 changed files with 249 additions and 123 deletions

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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: {

View File

@ -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
}

View File

@ -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
}

View File

@ -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
})
}

View File

@ -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

View File

@ -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()

View File

@ -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.
}
}

View File

@ -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

View File

@ -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

View File

@ -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)
}

View File

@ -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
}

View File

@ -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 {

View File

@ -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"

View File

@ -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"/>

View File

@ -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>

View File

@ -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>

View File

@ -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>