added date displaying for DM

improved DM time
This commit is contained in:
Mariotaku Lee 2017-02-11 14:50:17 +08:00
parent 7522bf04eb
commit f665318eb6
No known key found for this signature in database
GPG Key ID: 15C10F89D7C33535
12 changed files with 101 additions and 37 deletions

View File

@ -23,17 +23,20 @@ import android.content.Context
import android.support.v7.widget.RecyclerView import android.support.v7.widget.RecyclerView
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.ViewGroup import android.view.ViewGroup
import org.apache.commons.lang3.time.DateUtils
import org.mariotaku.twidere.adapter.iface.IItemCountsAdapter import org.mariotaku.twidere.adapter.iface.IItemCountsAdapter
import org.mariotaku.twidere.extension.model.timestamp
import org.mariotaku.twidere.model.ItemCounts import org.mariotaku.twidere.model.ItemCounts
import org.mariotaku.twidere.model.ParcelableMessage import org.mariotaku.twidere.model.ParcelableMessage
import org.mariotaku.twidere.model.ParcelableMessage.MessageType import org.mariotaku.twidere.model.ParcelableMessage.MessageType
import org.mariotaku.twidere.view.holder.message.AbsMessageViewHolder import org.mariotaku.twidere.view.holder.message.AbsMessageViewHolder
import org.mariotaku.twidere.view.holder.message.MessageViewHolder import org.mariotaku.twidere.view.holder.message.MessageViewHolder
import org.mariotaku.twidere.view.holder.message.StickerMessageViewHolder import org.mariotaku.twidere.view.holder.message.StickerMessageViewHolder
import java.util.*
class MessagesConversationAdapter(context: Context) : LoadMoreSupportAdapter<RecyclerView.ViewHolder>(context), class MessagesConversationAdapter(context: Context) : LoadMoreSupportAdapter<RecyclerView.ViewHolder>(context),
IItemCountsAdapter { IItemCountsAdapter {
private val calendars = Pair(Calendar.getInstance(), Calendar.getInstance())
override val itemCounts: ItemCounts = ItemCounts(1) override val itemCounts: ItemCounts = ItemCounts(1)
var messages: List<ParcelableMessage>? = null var messages: List<ParcelableMessage>? = null
set(value) { set(value) {
@ -59,14 +62,24 @@ class MessagesConversationAdapter(context: Context) : LoadMoreSupportAdapter<Rec
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
when (holder.itemViewType) { when (holder.itemViewType) {
ITEM_TYPE_TEXT_MESSAGE, ITEM_TYPE_STICKER_MESSAGE -> { ITEM_TYPE_TEXT_MESSAGE, ITEM_TYPE_STICKER_MESSAGE -> {
(holder as AbsMessageViewHolder).display(getMessage(position)!!) val message = getMessage(position)!!
// Display date for oldest item
var showDate = true
// ... or if current message is > 1 day newer than previous one
if (position < itemCounts.getItemStartPosition(ITEM_START_MESSAGE)
+ itemCounts[ITEM_START_MESSAGE] - 1) {
calendars.first.timeInMillis = getMessage(position + 1)!!.timestamp
calendars.second.timeInMillis = message.timestamp
showDate = !DateUtils.isSameDay(calendars.first, calendars.second)
}
(holder as AbsMessageViewHolder).display(message, showDate)
} }
} }
} }
override fun getItemCount(): Int { override fun getItemCount(): Int {
itemCounts[0] = messages?.size ?: 0 itemCounts[ITEM_START_MESSAGE] = messages?.size ?: 0
return itemCounts.itemCount return itemCounts.itemCount
} }
@ -76,7 +89,7 @@ class MessagesConversationAdapter(context: Context) : LoadMoreSupportAdapter<Rec
override fun getItemViewType(position: Int): Int { override fun getItemViewType(position: Int): Int {
when (itemCounts.getItemCountIndex(position)) { when (itemCounts.getItemCountIndex(position)) {
0 -> { ITEM_START_MESSAGE -> {
when (getMessage(position)!!.message_type) { when (getMessage(position)!!.message_type) {
MessageType.STICKER -> { MessageType.STICKER -> {
return ITEM_TYPE_STICKER_MESSAGE return ITEM_TYPE_STICKER_MESSAGE
@ -89,6 +102,8 @@ class MessagesConversationAdapter(context: Context) : LoadMoreSupportAdapter<Rec
} }
companion object { companion object {
private const val ITEM_START_MESSAGE = 0
const val ITEM_TYPE_TEXT_MESSAGE = 1 const val ITEM_TYPE_TEXT_MESSAGE = 1
const val ITEM_TYPE_STICKER_MESSAGE = 2 const val ITEM_TYPE_STICKER_MESSAGE = 2
} }

View File

@ -15,6 +15,7 @@ fun ParcelableMessageConversation.setFrom(message: ParcelableMessage, details: A
message_type = message.message_type message_type = message.message_type
message_timestamp = message.message_timestamp message_timestamp = message.message_timestamp
local_timestamp = message.local_timestamp local_timestamp = message.local_timestamp
sort_id = message.sort_id
text_unescaped = message.text_unescaped text_unescaped = message.text_unescaped
media = message.media media = message.media
spans = message.spans spans = message.spans

View File

@ -31,7 +31,7 @@ class MessagesConversationFragment : BaseFragment(), LoaderManager.LoaderCallbac
super.onActivityCreated(savedInstanceState) super.onActivityCreated(savedInstanceState)
adapter = MessagesConversationAdapter(context) adapter = MessagesConversationAdapter(context)
recyclerView.adapter = adapter recyclerView.adapter = adapter
recyclerView.layoutManager = FixedLinearLayoutManager(context, LinearLayoutManager.VERTICAL, false) recyclerView.layoutManager = FixedLinearLayoutManager(context, LinearLayoutManager.VERTICAL, true)
loaderManager.initLoader(0, null, this) loaderManager.initLoader(0, null, this)
} }
@ -46,7 +46,7 @@ class MessagesConversationFragment : BaseFragment(), LoaderManager.LoaderCallbac
loader.selection = Expression.and(Expression.equalsArgs(Messages.ACCOUNT_KEY), loader.selection = Expression.and(Expression.equalsArgs(Messages.ACCOUNT_KEY),
Expression.equalsArgs(Messages.CONVERSATION_ID)).sql Expression.equalsArgs(Messages.CONVERSATION_ID)).sql
loader.selectionArgs = arrayOf(accountKey.toString(), conversationId) loader.selectionArgs = arrayOf(accountKey.toString(), conversationId)
loader.sortOrder = OrderBy(Messages.LOCAL_TIMESTAMP, true).sql loader.sortOrder = OrderBy(Messages.SORT_ID, false).sql
return loader return loader
} }

View File

@ -61,7 +61,7 @@ class MessagesEntriesFragment : AbsContentListRecyclerViewFragment<MessagesEntri
loader.selection = Expression.inArgs(Conversations.ACCOUNT_KEY, accountKeys.size).sql loader.selection = Expression.inArgs(Conversations.ACCOUNT_KEY, accountKeys.size).sql
loader.selectionArgs = accountKeys.toStringArray() loader.selectionArgs = accountKeys.toStringArray()
loader.projection = Conversations.COLUMNS loader.projection = Conversations.COLUMNS
loader.sortOrder = OrderBy(Conversations.LOCAL_TIMESTAMP, false).sql loader.sortOrder = OrderBy(Conversations.SORT_ID, false).sql
return loader return loader
} }

View File

@ -1,5 +1,6 @@
package org.mariotaku.twidere.model.util package org.mariotaku.twidere.model.util
import android.support.annotation.FloatRange
import org.mariotaku.microblog.library.twitter.model.DirectMessage import org.mariotaku.microblog.library.twitter.model.DirectMessage
import org.mariotaku.twidere.model.ParcelableMessage import org.mariotaku.twidere.model.ParcelableMessage
import org.mariotaku.twidere.model.UserKey import org.mariotaku.twidere.model.UserKey
@ -11,15 +12,23 @@ import org.mariotaku.twidere.util.InternalTwitterContentUtils
* Created by mariotaku on 2017/2/9. * Created by mariotaku on 2017/2/9.
*/ */
object ParcelableMessageUtils { object ParcelableMessageUtils {
fun incomingMessage(accountKey: UserKey, message: DirectMessage): ParcelableMessage { fun incomingMessage(
val result = message(accountKey, message) accountKey: UserKey,
message: DirectMessage,
@FloatRange(from = 0.0, to = 1.0) sortIdAdj: Double = 0.0
): ParcelableMessage {
val result = message(accountKey, message, sortIdAdj)
result.is_outgoing = false result.is_outgoing = false
result.conversation_id = incomingConversationId(message.senderId, message.recipientId) result.conversation_id = incomingConversationId(message.senderId, message.recipientId)
return result return result
} }
fun outgoingMessage(accountKey: UserKey, message: DirectMessage): ParcelableMessage { fun outgoingMessage(
val result = message(accountKey, message) accountKey: UserKey,
message: DirectMessage,
@FloatRange(from = 0.0, to = 1.0) sortIdAdj: Double = 0.0
): ParcelableMessage {
val result = message(accountKey, message, sortIdAdj)
result.is_outgoing = true result.is_outgoing = true
result.conversation_id = outgoingConversationId(message.senderId, message.recipientId) result.conversation_id = outgoingConversationId(message.senderId, message.recipientId)
return result return result
@ -33,7 +42,11 @@ object ParcelableMessageUtils {
return "$senderId-$recipientId" return "$senderId-$recipientId"
} }
private fun message(accountKey: UserKey, message: DirectMessage): ParcelableMessage { private fun message(
accountKey: UserKey,
message: DirectMessage,
@FloatRange(from = 0.0, to = 1.0) sortIdAdj: Double = 0.0
): ParcelableMessage {
val result = ParcelableMessage() val result = ParcelableMessage()
result.account_key = accountKey result.account_key = accountKey
result.id = message.id result.id = message.id
@ -41,6 +54,7 @@ object ParcelableMessageUtils {
result.recipient_key = UserKeyUtils.fromUser(message.recipient) result.recipient_key = UserKeyUtils.fromUser(message.recipient)
result.message_timestamp = message.createdAt.time result.message_timestamp = message.createdAt.time
result.local_timestamp = result.message_timestamp result.local_timestamp = result.message_timestamp
result.sort_id = result.message_timestamp + (499 * sortIdAdj).toLong()
val (type, extras) = typeAndExtras(accountKey, message) val (type, extras) = typeAndExtras(accountKey, message)
val (text, spans) = InternalTwitterContentUtils.formatDirectMessageText(message) val (text, spans) = InternalTwitterContentUtils.formatDirectMessageText(message)

View File

@ -31,8 +31,10 @@ import org.mariotaku.twidere.util.content.ContentResolverUtils
* Created by mariotaku on 2017/2/8. * Created by mariotaku on 2017/2/8.
*/ */
class GetMessagesTask(context: Context) : BaseAbstractTask<RefreshTaskParam, Unit, (Boolean) -> Unit>(context) { class GetMessagesTask(
override fun doLongOperation(param: RefreshTaskParam) { context: Context
) : BaseAbstractTask<GetMessagesTask.RefreshMessagesTaskParam, Unit, (Boolean) -> Unit>(context) {
override fun doLongOperation(param: RefreshMessagesTaskParam) {
val accountKeys = param.accountKeys val accountKeys = param.accountKeys
val am = AccountManager.get(context) val am = AccountManager.get(context)
accountKeys.forEachIndexed { i, accountKey -> accountKeys.forEachIndexed { i, accountKey ->
@ -121,13 +123,13 @@ class GetMessagesTask(context: Context) : BaseAbstractTask<RefreshTaskParam, Uni
conversations.addLocalConversations(accountKey, conversationIds) conversations.addLocalConversations(accountKey, conversationIds)
received.forEach { dm -> received.forEachIndexed { i, dm ->
val message = ParcelableMessageUtils.incomingMessage(accountKey, dm) val message = ParcelableMessageUtils.incomingMessage(accountKey, dm, 1.0 - (i.toDouble() / received.size))
insertMessages.add(message) insertMessages.add(message)
conversations.addConversation(details, message, dm.sender, dm.recipient) conversations.addConversation(details, message, dm.sender, dm.recipient)
} }
sent.forEach { dm -> sent.forEachIndexed { i, dm ->
val message = ParcelableMessageUtils.outgoingMessage(accountKey, dm) val message = ParcelableMessageUtils.outgoingMessage(accountKey, dm, 1.0 - (i.toDouble() / sent.size))
insertMessages.add(message) insertMessages.add(message)
conversations.addConversation(details, message, dm.sender, dm.recipient) conversations.addConversation(details, message, dm.sender, dm.recipient)
} }
@ -225,7 +227,6 @@ class GetMessagesTask(context: Context) : BaseAbstractTask<RefreshTaskParam, Uni
getAccountKeys: () -> Array<UserKey> getAccountKeys: () -> Array<UserKey>
) : RefreshMessagesTaskParam(context, getAccountKeys) { ) : RefreshMessagesTaskParam(context, getAccountKeys) {
override val sinceIds: Array<String?>? override val sinceIds: Array<String?>?
get() { get() {
val keys = accounts.map { account -> val keys = accounts.map { account ->
@ -252,7 +253,6 @@ class GetMessagesTask(context: Context) : BaseAbstractTask<RefreshTaskParam, Uni
getAccountKeys: () -> Array<UserKey> getAccountKeys: () -> Array<UserKey>
) : RefreshMessagesTaskParam(context, getAccountKeys) { ) : RefreshMessagesTaskParam(context, getAccountKeys) {
override val maxIds: Array<String?>? override val maxIds: Array<String?>?
get() { get() {
val keys = accounts.map { account -> val keys = accounts.map { account ->
@ -279,6 +279,11 @@ class GetMessagesTask(context: Context) : BaseAbstractTask<RefreshTaskParam, Uni
val getAccountKeys: () -> Array<UserKey> val getAccountKeys: () -> Array<UserKey>
) : SimpleRefreshTaskParam() { ) : SimpleRefreshTaskParam() {
/**
* If `conversationId` has value, load messages in conversationId
*/
open var conversationId: String? = null
protected val accounts: Array<AccountDetails?> by lazy { protected val accounts: Array<AccountDetails?> by lazy {
AccountUtils.getAllAccountDetails(AccountManager.get(context), accountKeys, false) AccountUtils.getAllAccountDetails(AccountManager.get(context), accountKeys, false)
} }

View File

@ -240,7 +240,7 @@ class AsyncTwitterWrapper(
TaskStarter.execute<Any, Unit, Any>(task) TaskStarter.execute<Any, Unit, Any>(task)
} }
fun getMessagesAsync(param: RefreshTaskParam) { fun getMessagesAsync(param: GetMessagesTask.RefreshMessagesTaskParam) {
val task = GetMessagesTask(context) val task = GetMessagesTask(context)
task.params = param task.params = param
TaskStarter.execute(task) TaskStarter.execute(task)
@ -293,9 +293,7 @@ class AsyncTwitterWrapper(
}) })
} }
if (preferences.getBoolean(SharedPreferenceConstants.KEY_HOME_REFRESH_DIRECT_MESSAGES)) { if (preferences.getBoolean(SharedPreferenceConstants.KEY_HOME_REFRESH_DIRECT_MESSAGES)) {
getMessagesAsync(object : SimpleRefreshTaskParam() { getMessagesAsync(GetMessagesTask.RefreshMessagesTaskParam(context, action))
override val accountKeys: Array<UserKey> by lazy { action() }
})
} }
if (preferences.getBoolean(SharedPreferenceConstants.KEY_HOME_REFRESH_SAVED_SEARCHES)) { if (preferences.getBoolean(SharedPreferenceConstants.KEY_HOME_REFRESH_SAVED_SEARCHES)) {
getSavedSearchesAsync(action()) getSavedSearchesAsync(action())

View File

@ -1,6 +1,5 @@
package org.mariotaku.twidere.util package org.mariotaku.twidere.util
import android.annotation.SuppressLint
import android.content.Context import android.content.Context
import android.text.TextUtils.isEmpty import android.text.TextUtils.isEmpty
import okhttp3.ConnectionPool import okhttp3.ConnectionPool
@ -34,7 +33,6 @@ object HttpClientFactory {
DebugModeUtils.initForOkHttpClient(builder) DebugModeUtils.initForOkHttpClient(builder)
} }
@SuppressLint("SSLCertificateSocketFactoryGetInsecure")
internal fun updateHttpClientConfiguration(builder: OkHttpClient.Builder, internal fun updateHttpClientConfiguration(builder: OkHttpClient.Builder,
conf: HttpClientConfiguration, dns: Dns, conf: HttpClientConfiguration, dns: Dns,
connectionPool: ConnectionPool) { connectionPool: ConnectionPool) {

View File

@ -29,7 +29,8 @@ import org.mariotaku.twidere.model.ParcelableMessage
*/ */
abstract class AbsMessageViewHolder(itemView: View, val adapter: MessagesConversationAdapter) : RecyclerView.ViewHolder(itemView) { abstract class AbsMessageViewHolder(itemView: View, val adapter: MessagesConversationAdapter) : RecyclerView.ViewHolder(itemView) {
open fun display(message: ParcelableMessage) {
open fun display(message: ParcelableMessage, showDate: Boolean) {
} }
} }

View File

@ -20,8 +20,10 @@
package org.mariotaku.twidere.view.holder.message package org.mariotaku.twidere.view.holder.message
import android.support.v4.view.GravityCompat import android.support.v4.view.GravityCompat
import android.text.format.DateUtils
import android.view.View import android.view.View
import android.widget.FrameLayout import android.widget.FrameLayout
import android.widget.LinearLayout
import kotlinx.android.synthetic.main.list_item_message_conversation_text.view.* import kotlinx.android.synthetic.main.list_item_message_conversation_text.view.*
import org.mariotaku.ktextension.isNullOrEmpty import org.mariotaku.ktextension.isNullOrEmpty
import org.mariotaku.messagebubbleview.library.MessageBubbleView import org.mariotaku.messagebubbleview.library.MessageBubbleView
@ -36,16 +38,32 @@ import org.mariotaku.twidere.model.ParcelableMessage
class MessageViewHolder(itemView: View, adapter: MessagesConversationAdapter) : AbsMessageViewHolder(itemView, adapter) { class MessageViewHolder(itemView: View, adapter: MessagesConversationAdapter) : AbsMessageViewHolder(itemView, adapter) {
private val date by lazy { itemView.date }
private val text by lazy { itemView.text } private val text by lazy { itemView.text }
private val time by lazy { itemView.time } private val time by lazy { itemView.time }
private val mediaPreview by lazy { itemView.mediaPreview } private val mediaPreview by lazy { itemView.mediaPreview }
private val messageContent by lazy { itemView.messageContent } private val messageContent by lazy { itemView.messageContent }
override fun display(message: ParcelableMessage) { init {
super.display(message) val textSize = adapter.textSize
text.textSize = textSize
time.textSize = textSize * 0.8f
date.textSize = textSize * 0.9f
}
override fun display(message: ParcelableMessage, showDate: Boolean) {
super.display(message, showDate)
setOutgoingStatus(messageContent, message.is_outgoing) setOutgoingStatus(messageContent, message.is_outgoing)
text.text = message.text_unescaped text.text = message.text_unescaped
time.time = message.timestamp if (showDate) {
date.visibility = View.VISIBLE
date.text = DateUtils.getRelativeTimeSpanString(message.timestamp, System.currentTimeMillis(),
DateUtils.DAY_IN_MILLIS, DateUtils.FORMAT_SHOW_DATE)
} else {
date.visibility = View.GONE
}
time.text = DateUtils.formatDateTime(adapter.context, message.timestamp,
DateUtils.FORMAT_SHOW_TIME)
if (message.media.isNullOrEmpty()) { if (message.media.isNullOrEmpty()) {
mediaPreview.visibility = View.GONE mediaPreview.visibility = View.GONE
} else { } else {
@ -69,6 +87,9 @@ class MessageViewHolder(itemView: View, adapter: MessagesConversationAdapter) :
is FrameLayout.LayoutParams -> { is FrameLayout.LayoutParams -> {
lp.gravity = if (outgoing) GravityCompat.END else GravityCompat.START lp.gravity = if (outgoing) GravityCompat.END else GravityCompat.START
} }
is LinearLayout.LayoutParams -> {
lp.gravity = if (outgoing) GravityCompat.END else GravityCompat.START
}
} }
} }
} }

View File

@ -35,8 +35,8 @@ class StickerMessageViewHolder(itemView: View, adapter: MessagesConversationAdap
private val messageContent by lazy { itemView.messageContent } private val messageContent by lazy { itemView.messageContent }
private val stickerIcon by lazy { itemView.stickerIcon } private val stickerIcon by lazy { itemView.stickerIcon }
override fun display(message: ParcelableMessage) { override fun display(message: ParcelableMessage, showDate: Boolean) {
super.display(message) super.display(message, showDate)
MessageViewHolder.setMessageContentGravity(messageContent, message.is_outgoing) MessageViewHolder.setMessageContentGravity(messageContent, message.is_outgoing)
val extras = message.extras as StickerExtras val extras = message.extras as StickerExtras
adapter.mediaLoader.displayStickerImage(stickerIcon, extras.url) adapter.mediaLoader.displayStickerImage(stickerIcon, extras.url)

View File

@ -17,14 +17,25 @@
~ along with this program. If not, see <http://www.gnu.org/licenses/>. ~ along with this program. If not, see <http://www.gnu.org/licenses/>.
--> -->
<FrameLayout <LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android" xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="@dimen/element_spacing_normal"> android:padding="@dimen/element_spacing_normal">
<org.mariotaku.twidere.view.FixedTextView
android:id="@+id/date"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:padding="@dimen/element_spacing_normal"
android:textAppearance="?android:textAppearanceSmall"
android:textColor="?android:textColorTertiary"
tools:text="Yesterday"/>
<org.mariotaku.messagebubbleview.library.MessageBubbleView <org.mariotaku.messagebubbleview.library.MessageBubbleView
android:id="@+id/messageContent" android:id="@+id/messageContent"
android:layout_width="wrap_content" android:layout_width="wrap_content"
@ -63,18 +74,18 @@
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceSmall" android:textAppearance="?android:attr/textAppearanceSmall"
android:textColor="?android:attr/textColorSecondary" android:textColor="?android:attr/textColorPrimary"
tools:text="@string/sample_status_text"/> tools:text="@string/sample_status_text"/>
<org.mariotaku.twidere.view.ShortTimeView <org.mariotaku.twidere.view.FixedTextView
android:id="@+id/time" android:id="@+id/time"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="@dimen/element_spacing_small" android:layout_marginTop="@dimen/element_spacing_small"
android:textColor="?android:attr/textColorSecondary" android:textColor="?android:attr/textColorTertiary"
tools:text="12:00"/> tools:text="12:00"/>
</LinearLayout> </LinearLayout>
</RelativeLayout> </RelativeLayout>
</org.mariotaku.messagebubbleview.library.MessageBubbleView> </org.mariotaku.messagebubbleview.library.MessageBubbleView>
</FrameLayout> </LinearLayout>