fixed source file name

This commit is contained in:
Mariotaku Lee 2017-02-16 21:51:49 +08:00
parent fda48b3b9e
commit 43a80bdb70
No known key found for this signature in database
GPG Key ID: 15C10F89D7C33535
14 changed files with 137 additions and 215 deletions

View File

@ -37,6 +37,13 @@ public class NewDm extends SimpleValueMap {
put("conversation_id", conversationId); put("conversation_id", conversationId);
} }
/**
* Help you identify which is new message, this id will be attached in request result
*/
public void setRequestId(String requestId) {
put("request_id", requestId);
}
public void setRecipientIds(String[] recipientIds) { public void setRecipientIds(String[] recipientIds) {
put("recipient_ids", InternalArrayUtil.join(recipientIds, ",")); put("recipient_ids", InternalArrayUtil.join(recipientIds, ","));
} }

View File

@ -155,7 +155,6 @@ public interface TwidereConstants extends SharedPreferenceConstants, IntentConst
String QUERY_PARAM_NEW_ITEMS_COUNT = "new_items_count"; String QUERY_PARAM_NEW_ITEMS_COUNT = "new_items_count";
String QUERY_PARAM_CONVERSATION_ID = "conversation_id"; String QUERY_PARAM_CONVERSATION_ID = "conversation_id";
String QUERY_PARAM_READ_POSITION = "param_read_position"; String QUERY_PARAM_READ_POSITION = "param_read_position";
String QUERY_PARAM_READ_POSITIONS = "param_read_positions";
String QUERY_PARAM_LIMIT = "limit"; String QUERY_PARAM_LIMIT = "limit";
String QUERY_PARAM_EXTRA = "extra"; String QUERY_PARAM_EXTRA = "extra";
String QUERY_PARAM_TIMESTAMP = "timestamp"; String QUERY_PARAM_TIMESTAMP = "timestamp";

View File

@ -142,6 +142,10 @@ public class ParcelableMessageConversation implements Parcelable {
@CursorField(value = Conversations.REQUEST_CURSOR) @CursorField(value = Conversations.REQUEST_CURSOR)
public String request_cursor; public String request_cursor;
@JsonField(name = "last_read_id")
@CursorField(value = Conversations.LAST_READ_ID)
public String last_read_id;
/** /**
* True if this is a temporary conversation, i.e. Created by user but haven't send any message * True if this is a temporary conversation, i.e. Created by user but haven't send any message
* yet. * yet.

View File

@ -382,6 +382,7 @@ public interface TwidereDataStore {
String SENDER_KEY = "sender_key"; String SENDER_KEY = "sender_key";
String RECIPIENT_KEY = "recipient_key"; String RECIPIENT_KEY = "recipient_key";
String REQUEST_CURSOR = "request_cursor"; String REQUEST_CURSOR = "request_cursor";
String LAST_READ_ID = "last_read_id";
String IS_OUTGOING = "is_outgoing"; String IS_OUTGOING = "is_outgoing";
String IS_TEMP = "is_temp"; String IS_TEMP = "is_temp";
String CONVERSATION_EXTRAS = "conversation_extras"; String CONVERSATION_EXTRAS = "conversation_extras";

View File

@ -34,7 +34,7 @@ import static org.mariotaku.twidere.annotation.PreferenceType.STRING;
public interface Constants extends TwidereConstants { public interface Constants extends TwidereConstants {
String DATABASES_NAME = "twidere.sqlite"; String DATABASES_NAME = "twidere.sqlite";
int DATABASES_VERSION = 176; int DATABASES_VERSION = 177;
int EXTRA_FEATURES_NOTICE_VERSION = 0; int EXTRA_FEATURES_NOTICE_VERSION = 0;

View File

@ -22,12 +22,11 @@ package org.mariotaku.twidere.extension
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.content.ContentResolver import android.content.ContentResolver
import android.database.Cursor import android.database.Cursor
import android.net.Uri
import org.mariotaku.twidere.provider.TwidereDataStore import org.mariotaku.twidere.provider.TwidereDataStore
@SuppressLint("Recycle") @SuppressLint("Recycle")
fun ContentResolver.rawQuery(sql: String, selectionArgs: Array<String>?): Cursor { fun ContentResolver.rawQuery(sql: String, selectionArgs: Array<String>?): Cursor? {
val rawUri = Uri.withAppendedPath(TwidereDataStore.CONTENT_URI_RAW_QUERY, sql) val rawUri = TwidereDataStore.CONTENT_URI_RAW_QUERY.buildUpon().appendPath(sql).build()
return query(rawUri, null, null, selectionArgs, null) return query(rawUri, null, null, selectionArgs, null)
} }

View File

@ -541,7 +541,6 @@ class TwidereDataProvider : ContentProvider(), LazyLoadCallback {
val prefs = AccountPreferences.getNotificationEnabledPreferences(context, val prefs = AccountPreferences.getNotificationEnabledPreferences(context,
DataStoreUtils.getAccountKeys(context)) DataStoreUtils.getAccountKeys(context))
prefs.filter(AccountPreferences::isDirectMessagesNotificationEnabled).forEach { prefs.filter(AccountPreferences::isDirectMessagesNotificationEnabled).forEach {
val pairs = readStateManager.getPositionPairs(CustomTabType.DIRECT_MESSAGES)
// TODO show messages notifications // TODO show messages notifications
} }
notifyUnreadCountChanged(NOTIFICATION_ID_DIRECT_MESSAGES) notifyUnreadCountChanged(NOTIFICATION_ID_DIRECT_MESSAGES)

View File

@ -64,23 +64,12 @@ class NotificationReceiver : BroadcastReceiver() {
} }
val manager = holder.readStateManager val manager = holder.readStateManager
val paramReadPosition = uri.getQueryParameter(QUERY_PARAM_READ_POSITION) val paramReadPosition = uri.getQueryParameter(QUERY_PARAM_READ_POSITION)
val paramReadPositions = uri.getQueryParameter(QUERY_PARAM_READ_POSITIONS)
@ReadPositionTag @ReadPositionTag
val tag = getPositionTag(notificationType) val tag = getPositionTag(notificationType)
if (tag != null && !TextUtils.isEmpty(paramReadPosition)) { if (tag != null && !TextUtils.isEmpty(paramReadPosition)) {
manager.setPosition(Utils.getReadPositionTagWithAccount(tag, accountKey), manager.setPosition(Utils.getReadPositionTagWithAccount(tag, accountKey),
paramReadPosition.toLong(-1)) paramReadPosition.toLong(-1))
} else if (!TextUtils.isEmpty(paramReadPositions)) {
try {
val pairs = StringLongPair.valuesOf(paramReadPositions)
for (pair in pairs) {
manager.setPosition(tag!!, pair.key, pair.value)
}
} catch (ignore: NumberFormatException) {
}
} }
} }
} }

View File

@ -24,23 +24,31 @@ import android.content.ContentValues
import android.content.Context import android.content.Context
import org.mariotaku.ktextension.toInt import org.mariotaku.ktextension.toInt
import org.mariotaku.ktextension.useCursor import org.mariotaku.ktextension.useCursor
import org.mariotaku.microblog.library.MicroBlog
import org.mariotaku.microblog.library.MicroBlogException
import org.mariotaku.microblog.library.twitter.model.DMResponse import org.mariotaku.microblog.library.twitter.model.DMResponse
import org.mariotaku.microblog.library.twitter.model.Paging
import org.mariotaku.microblog.library.twitter.model.User import org.mariotaku.microblog.library.twitter.model.User
import org.mariotaku.microblog.library.twitter.model.fixMedia import org.mariotaku.microblog.library.twitter.model.fixMedia
import org.mariotaku.sqliteqb.library.Expression import org.mariotaku.sqliteqb.library.Expression
import org.mariotaku.twidere.annotation.AccountType
import org.mariotaku.twidere.extension.model.applyFrom import org.mariotaku.twidere.extension.model.applyFrom
import org.mariotaku.twidere.extension.model.isOfficial import org.mariotaku.twidere.extension.model.isOfficial
import org.mariotaku.twidere.extension.model.newMicroBlogInstance import org.mariotaku.twidere.extension.model.newMicroBlogInstance
import org.mariotaku.twidere.extension.model.timestamp import org.mariotaku.twidere.extension.model.timestamp
import org.mariotaku.twidere.model.* import org.mariotaku.twidere.model.*
import org.mariotaku.twidere.model.ParcelableMessageConversation.ConversationType import org.mariotaku.twidere.model.ParcelableMessageConversation.ConversationType
import org.mariotaku.twidere.model.event.GetMessagesTaskEvent
import org.mariotaku.twidere.model.message.conversation.TwitterOfficialConversationExtras import org.mariotaku.twidere.model.message.conversation.TwitterOfficialConversationExtras
import org.mariotaku.twidere.model.util.AccountUtils
import org.mariotaku.twidere.model.util.AccountUtils.getAccountDetails import org.mariotaku.twidere.model.util.AccountUtils.getAccountDetails
import org.mariotaku.twidere.model.util.ParcelableMessageUtils
import org.mariotaku.twidere.model.util.ParcelableUserUtils import org.mariotaku.twidere.model.util.ParcelableUserUtils
import org.mariotaku.twidere.model.util.UserKeyUtils import org.mariotaku.twidere.model.util.UserKeyUtils
import org.mariotaku.twidere.provider.TwidereDataStore.Messages import org.mariotaku.twidere.provider.TwidereDataStore.Messages
import org.mariotaku.twidere.provider.TwidereDataStore.Messages.Conversations import org.mariotaku.twidere.provider.TwidereDataStore.Messages.Conversations
import org.mariotaku.twidere.task.BaseAbstractTask import org.mariotaku.twidere.task.BaseAbstractTask
import org.mariotaku.twidere.util.DataStoreUtils
import org.mariotaku.twidere.util.content.ContentResolverUtils import org.mariotaku.twidere.util.content.ContentResolverUtils
import java.util.* import java.util.*
@ -56,10 +64,10 @@ class GetMessagesTask(
val am = android.accounts.AccountManager.get(context) val am = android.accounts.AccountManager.get(context)
accountKeys.forEachIndexed { i, accountKey -> accountKeys.forEachIndexed { i, accountKey ->
val details = getAccountDetails(am, accountKey, true) ?: return@forEachIndexed val details = getAccountDetails(am, accountKey, true) ?: return@forEachIndexed
val microBlog = details.newMicroBlogInstance(context, true, cls = org.mariotaku.microblog.library.MicroBlog::class.java) val microBlog = details.newMicroBlogInstance(context, true, cls = MicroBlog::class.java)
val messages = try { val messages = try {
getMessages(microBlog, details, param, i) getMessages(microBlog, details, param, i)
} catch (e: org.mariotaku.microblog.library.MicroBlogException) { } catch (e: MicroBlogException) {
return@forEachIndexed return@forEachIndexed
} }
Companion.storeMessages(context, messages, details) Companion.storeMessages(context, messages, details)
@ -68,16 +76,16 @@ class GetMessagesTask(
override fun afterExecute(callback: ((Boolean) -> Unit)?, result: Unit) { override fun afterExecute(callback: ((Boolean) -> Unit)?, result: Unit) {
callback?.invoke(true) callback?.invoke(true)
bus.post(org.mariotaku.twidere.model.event.GetMessagesTaskEvent(Messages.CONTENT_URI, params?.taskTag, false, null)) bus.post(GetMessagesTaskEvent(Messages.CONTENT_URI, params?.taskTag, false, null))
} }
private fun getMessages(microBlog: org.mariotaku.microblog.library.MicroBlog, details: AccountDetails, param: RefreshMessagesTaskParam, index: Int): DatabaseUpdateData { private fun getMessages(microBlog: MicroBlog, details: AccountDetails, param: RefreshMessagesTaskParam, index: Int): DatabaseUpdateData {
when (details.type) { when (details.type) {
org.mariotaku.twidere.annotation.AccountType.FANFOU -> { AccountType.FANFOU -> {
// Use fanfou DM api, disabled since it's conversation api is not suitable for paging // Use fanfou DM api, disabled since it's conversation api is not suitable for paging
// return getFanfouMessages(microBlog, details, param, index) // return getFanfouMessages(microBlog, details, param, index)
} }
org.mariotaku.twidere.annotation.AccountType.TWITTER -> { AccountType.TWITTER -> {
// Use official DM api // Use official DM api
if (details.isOfficial(context)) { if (details.isOfficial(context)) {
return getTwitterOfficialMessages(microBlog, details, param, index) return getTwitterOfficialMessages(microBlog, details, param, index)
@ -88,7 +96,7 @@ class GetMessagesTask(
return getDefaultMessages(microBlog, details, param, index) return getDefaultMessages(microBlog, details, param, index)
} }
private fun getTwitterOfficialMessages(microBlog: org.mariotaku.microblog.library.MicroBlog, details: AccountDetails, private fun getTwitterOfficialMessages(microBlog: MicroBlog, details: AccountDetails,
param: RefreshMessagesTaskParam, index: Int): DatabaseUpdateData { param: RefreshMessagesTaskParam, index: Int): DatabaseUpdateData {
val conversationId = param.conversationId val conversationId = param.conversationId
if (conversationId == null) { if (conversationId == null) {
@ -98,7 +106,7 @@ class GetMessagesTask(
} }
} }
private fun getFanfouMessages(microBlog: org.mariotaku.microblog.library.MicroBlog, details: AccountDetails, param: RefreshMessagesTaskParam, index: Int): DatabaseUpdateData { private fun getFanfouMessages(microBlog: MicroBlog, details: AccountDetails, param: RefreshMessagesTaskParam, index: Int): DatabaseUpdateData {
val conversationId = param.conversationId val conversationId = param.conversationId
if (conversationId == null) { if (conversationId == null) {
return getFanfouConversations(microBlog, details, param, index) return getFanfouConversations(microBlog, details, param, index)
@ -107,13 +115,13 @@ class GetMessagesTask(
} }
} }
private fun getDefaultMessages(microBlog: org.mariotaku.microblog.library.MicroBlog, details: AccountDetails, param: RefreshMessagesTaskParam, index: Int): DatabaseUpdateData { private fun getDefaultMessages(microBlog: MicroBlog, details: AccountDetails, param: RefreshMessagesTaskParam, index: Int): DatabaseUpdateData {
val accountKey = details.key val accountKey = details.key
val sinceIds = if (param.hasSinceIds) param.sinceIds else null val sinceIds = if (param.hasSinceIds) param.sinceIds else null
val maxIds = if (param.hasMaxIds) param.maxIds else null val maxIds = if (param.hasMaxIds) param.maxIds else null
val received = microBlog.getDirectMessages(org.mariotaku.microblog.library.twitter.model.Paging().apply { val received = microBlog.getDirectMessages(Paging().apply {
count(100) count(100)
val maxId = maxIds?.get(index) val maxId = maxIds?.get(index)
val sinceId = sinceIds?.get(index) val sinceId = sinceIds?.get(index)
@ -124,7 +132,7 @@ class GetMessagesTask(
sinceId(sinceId) sinceId(sinceId)
} }
}) })
val sent = microBlog.getSentDirectMessages(org.mariotaku.microblog.library.twitter.model.Paging().apply { val sent = microBlog.getSentDirectMessages(Paging().apply {
count(100) count(100)
val accountsCount = param.accountKeys.size val accountsCount = param.accountKeys.size
val maxId = maxIds?.get(accountsCount + index) val maxId = maxIds?.get(accountsCount + index)
@ -143,22 +151,22 @@ class GetMessagesTask(
val conversationIds = hashSetOf<String>() val conversationIds = hashSetOf<String>()
received.forEach { received.forEach {
conversationIds.add(org.mariotaku.twidere.model.util.ParcelableMessageUtils.incomingConversationId(it.senderId, it.recipientId)) conversationIds.add(ParcelableMessageUtils.incomingConversationId(it.senderId, it.recipientId))
} }
sent.forEach { sent.forEach {
conversationIds.add(org.mariotaku.twidere.model.util.ParcelableMessageUtils.outgoingConversationId(it.senderId, it.recipientId)) conversationIds.add(ParcelableMessageUtils.outgoingConversationId(it.senderId, it.recipientId))
} }
conversations.addLocalConversations(context, accountKey, conversationIds) conversations.addLocalConversations(context, accountKey, conversationIds)
received.forEachIndexed { i, dm -> received.forEachIndexed { i, dm ->
val message = org.mariotaku.twidere.model.util.ParcelableMessageUtils.fromMessage(accountKey, dm, false, val message = ParcelableMessageUtils.fromMessage(accountKey, dm, false,
1.0 - (i.toDouble() / received.size)) 1.0 - (i.toDouble() / received.size))
insertMessages.add(message) insertMessages.add(message)
conversations.addConversation(message.conversation_id, details, message, setOf(dm.sender, dm.recipient)) conversations.addConversation(message.conversation_id, details, message, setOf(dm.sender, dm.recipient))
} }
sent.forEachIndexed { i, dm -> sent.forEachIndexed { i, dm ->
val message = org.mariotaku.twidere.model.util.ParcelableMessageUtils.fromMessage(accountKey, dm, true, val message = ParcelableMessageUtils.fromMessage(accountKey, dm, true,
1.0 - (i.toDouble() / sent.size)) 1.0 - (i.toDouble() / sent.size))
insertMessages.add(message) insertMessages.add(message)
conversations.addConversation(message.conversation_id, details, message, setOf(dm.sender, dm.recipient)) conversations.addConversation(message.conversation_id, details, message, setOf(dm.sender, dm.recipient))
@ -166,10 +174,10 @@ class GetMessagesTask(
return DatabaseUpdateData(conversations.values, insertMessages) return DatabaseUpdateData(conversations.values, insertMessages)
} }
private fun getTwitterOfficialConversation(microBlog: org.mariotaku.microblog.library.MicroBlog, details: AccountDetails, private fun getTwitterOfficialConversation(microBlog: MicroBlog, details: AccountDetails,
conversationId: String, param: RefreshMessagesTaskParam, index: Int): DatabaseUpdateData { conversationId: String, param: RefreshMessagesTaskParam, index: Int): DatabaseUpdateData {
val maxId = param.maxIds?.get(index) ?: return DatabaseUpdateData(emptyList(), emptyList()) val maxId = param.maxIds?.get(index) ?: return DatabaseUpdateData(emptyList(), emptyList())
val paging = org.mariotaku.microblog.library.twitter.model.Paging().apply { val paging = Paging().apply {
maxId(maxId) maxId(maxId)
} }
@ -178,14 +186,14 @@ class GetMessagesTask(
return Companion.createDatabaseUpdateData(context, details, response) return Companion.createDatabaseUpdateData(context, details, response)
} }
private fun getTwitterOfficialUserInbox(microBlog: org.mariotaku.microblog.library.MicroBlog, details: AccountDetails, private fun getTwitterOfficialUserInbox(microBlog: MicroBlog, details: AccountDetails,
param: RefreshMessagesTaskParam, index: Int): DatabaseUpdateData { param: RefreshMessagesTaskParam, index: Int): DatabaseUpdateData {
val maxId = if (param.hasMaxIds) param.maxIds?.get(index) else null val maxId = if (param.hasMaxIds) param.maxIds?.get(index) else null
val cursor = if (param.hasCursors) param.cursors?.get(index) else null val cursor = if (param.hasCursors) param.cursors?.get(index) else null
val response = if (cursor != null) { val response = if (cursor != null) {
microBlog.getUserUpdates(cursor).userEvents microBlog.getUserUpdates(cursor).userEvents
} else { } else {
microBlog.getUserInbox(org.mariotaku.microblog.library.twitter.model.Paging().apply { microBlog.getUserInbox(Paging().apply {
if (maxId != null) { if (maxId != null) {
maxId(maxId) maxId(maxId)
} }
@ -196,11 +204,11 @@ class GetMessagesTask(
} }
private fun getFanfouConversations(microBlog: org.mariotaku.microblog.library.MicroBlog, details: AccountDetails, param: RefreshMessagesTaskParam, index: Int): DatabaseUpdateData { private fun getFanfouConversations(microBlog: MicroBlog, details: AccountDetails, param: RefreshMessagesTaskParam, index: Int): DatabaseUpdateData {
val accountKey = details.key val accountKey = details.key
val cursor = param.cursors?.get(index) val cursor = param.cursors?.get(index)
val page = cursor?.substringAfter("page:").toInt(-1) val page = cursor?.substringAfter("page:").toInt(-1)
val result = microBlog.getConversationList(org.mariotaku.microblog.library.twitter.model.Paging().apply { val result = microBlog.getConversationList(Paging().apply {
count(60) count(60)
if (page >= 0) { if (page >= 0) {
page(page) page(page)
@ -214,7 +222,7 @@ class GetMessagesTask(
result.forEachIndexed { i, item -> result.forEachIndexed { i, item ->
val dm = item.dm val dm = item.dm
// Sender is our self, treat as outgoing message // Sender is our self, treat as outgoing message
val message = org.mariotaku.twidere.model.util.ParcelableMessageUtils.fromMessage(accountKey, dm, dm.senderId == accountKey.id, val message = ParcelableMessageUtils.fromMessage(accountKey, dm, dm.senderId == accountKey.id,
1.0 - (i.toDouble() / result.size)) 1.0 - (i.toDouble() / result.size))
val mc = conversations.addConversation(message.conversation_id, details, message, val mc = conversations.addConversation(message.conversation_id, details, message,
setOf(dm.sender, dm.recipient)) setOf(dm.sender, dm.recipient))
@ -237,9 +245,9 @@ class GetMessagesTask(
override val sinceIds: Array<String?>? override val sinceIds: Array<String?>?
get() { get() {
val incomingIds = org.mariotaku.twidere.util.DataStoreUtils.getNewestMessageIds(context, Messages.CONTENT_URI, val incomingIds = DataStoreUtils.getNewestMessageIds(context, Messages.CONTENT_URI,
defaultKeys, false) defaultKeys, false)
val outgoingIds = org.mariotaku.twidere.util.DataStoreUtils.getNewestMessageIds(context, Messages.CONTENT_URI, val outgoingIds = DataStoreUtils.getNewestMessageIds(context, Messages.CONTENT_URI,
defaultKeys, true) defaultKeys, true)
return incomingIds + outgoingIds return incomingIds + outgoingIds
} }
@ -247,7 +255,7 @@ class GetMessagesTask(
override val cursors: Array<String?>? override val cursors: Array<String?>?
get() { get() {
val cursors = arrayOfNulls<String>(defaultKeys.size) val cursors = arrayOfNulls<String>(defaultKeys.size)
val newestConversations = org.mariotaku.twidere.util.DataStoreUtils.getNewestConversations(context, val newestConversations = DataStoreUtils.getNewestConversations(context,
Conversations.CONTENT_URI, twitterOfficialKeys) Conversations.CONTENT_URI, twitterOfficialKeys)
newestConversations.forEachIndexed { i, conversation -> newestConversations.forEachIndexed { i, conversation ->
cursors[i] = conversation?.request_cursor cursors[i] = conversation?.request_cursor
@ -265,11 +273,11 @@ class GetMessagesTask(
) : RefreshMessagesTaskParam(context) { ) : RefreshMessagesTaskParam(context) {
override val maxIds: Array<String?>? by lazy { override val maxIds: Array<String?>? by lazy {
val incomingIds = org.mariotaku.twidere.util.DataStoreUtils.getOldestMessageIds(context, Messages.CONTENT_URI, val incomingIds = DataStoreUtils.getOldestMessageIds(context, Messages.CONTENT_URI,
defaultKeys, false) defaultKeys, false)
val outgoingIds = org.mariotaku.twidere.util.DataStoreUtils.getOldestMessageIds(context, Messages.CONTENT_URI, val outgoingIds = DataStoreUtils.getOldestMessageIds(context, Messages.CONTENT_URI,
defaultKeys, true) defaultKeys, true)
val oldestConversations = org.mariotaku.twidere.util.DataStoreUtils.getOldestConversations(context, val oldestConversations = DataStoreUtils.getOldestConversations(context,
Conversations.CONTENT_URI, twitterOfficialKeys) Conversations.CONTENT_URI, twitterOfficialKeys)
oldestConversations.forEachIndexed { i, conversation -> oldestConversations.forEachIndexed { i, conversation ->
val extras = conversation?.conversation_extras as? TwitterOfficialConversationExtras ?: return@forEachIndexed val extras = conversation?.conversation_extras as? TwitterOfficialConversationExtras ?: return@forEachIndexed
@ -305,13 +313,13 @@ class GetMessagesTask(
var taskTag: String? = null var taskTag: String? = null
protected val accounts: Array<AccountDetails?> by lazy { protected val accounts: Array<AccountDetails?> by lazy {
org.mariotaku.twidere.model.util.AccountUtils.getAllAccountDetails(android.accounts.AccountManager.get(context), accountKeys, false) AccountUtils.getAllAccountDetails(android.accounts.AccountManager.get(context), accountKeys, false)
} }
protected val defaultKeys: Array<UserKey?>by lazy { protected val defaultKeys: Array<UserKey?>by lazy {
return@lazy accounts.map { account -> return@lazy accounts.map { account ->
account ?: return@map null account ?: return@map null
if (account.isOfficial(context) || account.type == org.mariotaku.twidere.annotation.AccountType.FANFOU) { if (account.isOfficial(context) || account.type == AccountType.FANFOU) {
return@map null return@map null
} }
return@map account.key return@map account.key
@ -358,7 +366,7 @@ class GetMessagesTask(
return@mapNotNullTo null return@mapNotNullTo null
} }
else -> { else -> {
return@mapNotNullTo org.mariotaku.twidere.model.util.ParcelableMessageUtils.fromEntry(account.key, entry, respUsers) return@mapNotNullTo ParcelableMessageUtils.fromEntry(account.key, entry, respUsers)
} }
} }
} }
@ -379,6 +387,7 @@ class GetMessagesTask(
conversation.conversation_avatar = v.avatarImageHttps conversation.conversation_avatar = v.avatarImageHttps
conversation.request_cursor = response.cursor conversation.request_cursor = response.cursor
conversation.conversation_extras_type = ParcelableMessageConversation.ExtrasType.TWITTER_OFFICIAL conversation.conversation_extras_type = ParcelableMessageConversation.ExtrasType.TWITTER_OFFICIAL
conversation.last_read_id = v.lastReadEventId
conversation.conversation_extras = TwitterOfficialConversationExtras().apply { conversation.conversation_extras = TwitterOfficialConversationExtras().apply {
this.minEntryId = v.minEntryId this.minEntryId = v.minEntryId
this.maxEntryId = v.maxEntryId this.maxEntryId = v.maxEntryId

View File

@ -38,10 +38,10 @@ import org.mariotaku.twidere.model.event.SendMessageTaskEvent
import org.mariotaku.twidere.model.util.ParcelableMessageUtils import org.mariotaku.twidere.model.util.ParcelableMessageUtils
import org.mariotaku.twidere.provider.TwidereDataStore.Messages.Conversations import org.mariotaku.twidere.provider.TwidereDataStore.Messages.Conversations
import org.mariotaku.twidere.task.ExceptionHandlingAbstractTask import org.mariotaku.twidere.task.ExceptionHandlingAbstractTask
import org.mariotaku.twidere.task.twitter.UpdateStatusTask
import org.mariotaku.twidere.task.twitter.message.GetMessagesTask import org.mariotaku.twidere.task.twitter.message.GetMessagesTask
import org.mariotaku.twidere.task.twitter.message.GetMessagesTask.Companion.addConversation import org.mariotaku.twidere.task.twitter.message.GetMessagesTask.Companion.addConversation
import org.mariotaku.twidere.task.twitter.message.GetMessagesTask.Companion.addLocalConversations import org.mariotaku.twidere.task.twitter.message.GetMessagesTask.Companion.addLocalConversations
import org.mariotaku.twidere.task.twitter.UpdateStatusTask
/** /**
* Created by mariotaku on 2017/2/8. * Created by mariotaku on 2017/2/8.
@ -88,7 +88,8 @@ class SendMessageTask(
return sendDefaultDM(microBlog, account, message) return sendDefaultDM(microBlog, account, message)
} }
private fun sendTwitterOfficialDM(microBlog: MicroBlog, account: AccountDetails, message: ParcelableNewMessage): GetMessagesTask.DatabaseUpdateData { private fun sendTwitterOfficialDM(microBlog: MicroBlog, account: AccountDetails,
message: ParcelableNewMessage): GetMessagesTask.DatabaseUpdateData {
var deleteOnSuccess: List<UpdateStatusTask.MediaDeletionItem>? = null var deleteOnSuccess: List<UpdateStatusTask.MediaDeletionItem>? = null
var deleteAlways: List<UpdateStatusTask.MediaDeletionItem>? = null var deleteAlways: List<UpdateStatusTask.MediaDeletionItem>? = null
val sendResponse = try { val sendResponse = try {

View File

@ -43,7 +43,6 @@ import org.mariotaku.twidere.constant.nameFirstKey
import org.mariotaku.twidere.extension.rawQuery import org.mariotaku.twidere.extension.rawQuery
import org.mariotaku.twidere.model.* import org.mariotaku.twidere.model.*
import org.mariotaku.twidere.model.util.ParcelableActivityUtils import org.mariotaku.twidere.model.util.ParcelableActivityUtils
import org.mariotaku.twidere.provider.TwidereDataStore
import org.mariotaku.twidere.provider.TwidereDataStore.Activities import org.mariotaku.twidere.provider.TwidereDataStore.Activities
import org.mariotaku.twidere.provider.TwidereDataStore.Statuses import org.mariotaku.twidere.provider.TwidereDataStore.Statuses
import org.mariotaku.twidere.receiver.NotificationReceiver import org.mariotaku.twidere.receiver.NotificationReceiver
@ -65,6 +64,81 @@ class ContentNotificationManager(
private var nameFirst: Boolean = false private var nameFirst: Boolean = false
private var useStarForLikes: Boolean = false private var useStarForLikes: Boolean = false
fun showTimeline(pref: AccountPreferences, minPositionKey: Long) {
val accountKey = pref.accountKey
val resources = context.resources
val nm = notificationManager
val selection = Expression.and(Expression.equalsArgs(Statuses.ACCOUNT_KEY),
Expression.greaterThan(Statuses.POSITION_KEY, minPositionKey))
val filteredSelection = buildStatusFilterWhereClause(preferences, Statuses.TABLE_NAME,
selection)
val selectionArgs = arrayOf(accountKey.toString())
val userProjection = arrayOf(Statuses.USER_KEY, Statuses.USER_NAME, Statuses.USER_SCREEN_NAME)
val statusProjection = arrayOf(Statuses.POSITION_KEY)
val statusCursor = context.contentResolver.query(Statuses.CONTENT_URI, statusProjection,
filteredSelection.sql, selectionArgs, Statuses.DEFAULT_SORT_ORDER) ?: return
val userCursor = context.contentResolver.rawQuery(SQLQueryBuilder.select(Columns(*userProjection))
.from(Table(Statuses.TABLE_NAME))
.where(filteredSelection)
.groupBy(Columns.Column(Statuses.USER_KEY))
.orderBy(OrderBy(Statuses.DEFAULT_SORT_ORDER)).buildSQL(), selectionArgs) ?: return
try {
val usersCount = userCursor.count
val statusesCount = statusCursor.count
if (statusesCount == 0 || usersCount == 0) return
val statusIndices = ParcelableStatusCursorIndices(statusCursor)
val userIndices = ParcelableStatusCursorIndices(userCursor)
val positionKey = if (statusCursor.moveToFirst()) statusCursor.getLong(statusIndices.position_key) else -1L
val notificationTitle = resources.getQuantityString(R.plurals.N_new_statuses,
statusesCount, statusesCount)
val notificationContent: String
userCursor.moveToFirst()
val displayName = userColorNameManager.getDisplayName(userCursor.getString(userIndices.user_key),
userCursor.getString(userIndices.user_name), userCursor.getString(userIndices.user_screen_name),
nameFirst)
if (usersCount == 1) {
notificationContent = context.getString(R.string.from_name, displayName)
} else if (usersCount == 2) {
userCursor.moveToPosition(1)
val othersName = userColorNameManager.getDisplayName(userCursor.getString(userIndices.user_key),
userCursor.getString(userIndices.user_name), userCursor.getString(userIndices.user_screen_name),
nameFirst)
notificationContent = resources.getString(R.string.from_name_and_name, displayName, othersName)
} else {
notificationContent = resources.getString(R.string.from_name_and_N_others, displayName, usersCount - 1)
}
// Setup notification
val builder = NotificationCompat.Builder(context)
builder.setAutoCancel(true)
builder.setSmallIcon(R.drawable.ic_stat_twitter)
builder.setTicker(notificationTitle)
builder.setContentTitle(notificationTitle)
builder.setContentText(notificationContent)
builder.setCategory(NotificationCompat.CATEGORY_SOCIAL)
builder.setContentIntent(getContentIntent(context, CustomTabType.HOME_TIMELINE,
NotificationType.HOME_TIMELINE, accountKey, positionKey))
builder.setDeleteIntent(getMarkReadDeleteIntent(context, NotificationType.HOME_TIMELINE,
accountKey, positionKey, false))
builder.setNumber(statusesCount)
builder.setCategory(NotificationCompat.CATEGORY_SOCIAL)
applyNotificationPreferences(builder, pref, pref.homeTimelineNotificationType)
try {
nm.notify("home_" + accountKey, Utils.getNotificationId(NOTIFICATION_ID_HOME_TIMELINE, accountKey), builder.build())
Utils.sendPebbleNotification(context, null, notificationContent)
} catch (e: SecurityException) {
// Silently ignore
}
} finally {
statusCursor.close()
userCursor.close()
}
}
fun showInteractions(pref: AccountPreferences, position: Long) { fun showInteractions(pref: AccountPreferences, position: Long) {
val cr = context.contentResolver val cr = context.contentResolver
val accountKey = pref.accountKey val accountKey = pref.accountKey
@ -163,12 +237,9 @@ class ContentNotificationManager(
} }
val notificationId = Utils.getNotificationId(NOTIFICATION_ID_INTERACTIONS_TIMELINE, accountKey) val notificationId = Utils.getNotificationId(NOTIFICATION_ID_INTERACTIONS_TIMELINE, accountKey)
notificationManager.notify("interactions", notificationId, builder.build()) notificationManager.notify("interactions", notificationId, builder.build())
Utils.sendPebbleNotification(context, context.getString(R.string.interactions), pebbleNotificationStringBuilder.toString())
Utils.sendPebbleNotification(context, context.resources.getString(R.string.interactions), pebbleNotificationStringBuilder.toString())
} }
private fun applyNotificationPreferences(builder: NotificationCompat.Builder, pref: AccountPreferences, defaultFlags: Int) { private fun applyNotificationPreferences(builder: NotificationCompat.Builder, pref: AccountPreferences, defaultFlags: Int) {
var notificationDefaults = 0 var notificationDefaults = 0
if (AccountPreferences.isNotificationHasLight(defaultFlags)) { if (AccountPreferences.isNotificationHasLight(defaultFlags)) {
@ -195,80 +266,6 @@ class ContentNotificationManager(
return !activityTracker.isHomeActivityStarted return !activityTracker.isHomeActivityStarted
} }
fun showTimeline(pref: AccountPreferences, position: Long) {
val accountKey = pref.accountKey
val resources = context.resources
val nm = notificationManager
val selection = Expression.and(Expression.equalsArgs(Statuses.ACCOUNT_KEY),
Expression.greaterThan(Statuses.POSITION_KEY, position))
val filteredSelection = buildStatusFilterWhereClause(preferences,
Statuses.TABLE_NAME, selection)
val selectionArgs = arrayOf(accountKey.toString())
val userProjection = arrayOf(Statuses.USER_KEY, Statuses.USER_NAME, Statuses.USER_SCREEN_NAME)
val statusProjection = arrayOf(Statuses.POSITION_KEY)
val statusCursor = context.contentResolver.query(Statuses.CONTENT_URI, statusProjection,
filteredSelection.sql, selectionArgs, Statuses.DEFAULT_SORT_ORDER)
val userCursor = context.contentResolver.rawQuery(SQLQueryBuilder.select(Columns(*userProjection))
.from(Table(Statuses.TABLE_NAME))
.where(filteredSelection)
.groupBy(Columns.Column(Statuses.USER_KEY))
.orderBy(OrderBy(Statuses.DEFAULT_SORT_ORDER)).buildSQL(), selectionArgs)
try {
val usersCount = userCursor.count
val statusesCount = statusCursor.count
if (statusesCount == 0 || usersCount == 0) return
val statusIndices = ParcelableStatusCursorIndices(statusCursor)
val userIndices = ParcelableStatusCursorIndices(userCursor)
val positionKey = if (statusCursor.moveToFirst()) statusCursor.getLong(statusIndices.position_key) else -1L
val notificationTitle = resources.getQuantityString(R.plurals.N_new_statuses,
statusesCount, statusesCount)
val notificationContent: String
userCursor.moveToFirst()
val displayName = userColorNameManager.getDisplayName(userCursor.getString(userIndices.user_key),
userCursor.getString(userIndices.user_name), userCursor.getString(userIndices.user_screen_name),
nameFirst)
if (usersCount == 1) {
notificationContent = context.getString(R.string.from_name, displayName)
} else if (usersCount == 2) {
userCursor.moveToPosition(1)
val othersName = userColorNameManager.getDisplayName(userCursor.getString(userIndices.user_key),
userCursor.getString(userIndices.user_name), userCursor.getString(userIndices.user_screen_name),
nameFirst)
notificationContent = resources.getString(R.string.from_name_and_name, displayName, othersName)
} else {
notificationContent = resources.getString(R.string.from_name_and_N_others, displayName, usersCount - 1)
}
// Setup notification
val builder = NotificationCompat.Builder(context)
builder.setAutoCancel(true)
builder.setSmallIcon(R.drawable.ic_stat_twitter)
builder.setTicker(notificationTitle)
builder.setContentTitle(notificationTitle)
builder.setContentText(notificationContent)
builder.setCategory(NotificationCompat.CATEGORY_SOCIAL)
builder.setContentIntent(getContentIntent(context, CustomTabType.HOME_TIMELINE,
NotificationType.HOME_TIMELINE, accountKey, positionKey))
builder.setDeleteIntent(getMarkReadDeleteIntent(context, NotificationType.HOME_TIMELINE,
accountKey, positionKey, false))
builder.setNumber(statusesCount)
builder.setCategory(NotificationCompat.CATEGORY_SOCIAL)
applyNotificationPreferences(builder, pref, pref.homeTimelineNotificationType)
try {
nm.notify("home_" + accountKey, Utils.getNotificationId(NOTIFICATION_ID_HOME_TIMELINE, accountKey), builder.build())
Utils.sendPebbleNotification(context, null, notificationContent)
} catch (e: SecurityException) {
// Silently ignore
}
} finally {
statusCursor.close()
userCursor.close()
}
}
private fun getContentIntent(context: Context, @CustomTabType type: String, private fun getContentIntent(context: Context, @CustomTabType type: String,
@NotificationType notificationType: String, accountKey: UserKey?, readPosition: Long): PendingIntent { @NotificationType notificationType: String, accountKey: UserKey?, readPosition: Long): PendingIntent {
@ -302,26 +299,6 @@ class ContentNotificationManager(
useStarForLikes = preferences[iWantMyStarsBackKey] useStarForLikes = preferences[iWantMyStarsBackKey]
} }
private fun getMarkReadDeleteIntent(context: Context, @NotificationType notificationType: String,
accountKey: UserKey?, positions: Array<StringLongPair>): PendingIntent {
// Setup delete intent
val intent = Intent(context, NotificationReceiver::class.java)
val linkBuilder = Uri.Builder()
linkBuilder.scheme(SCHEME_TWIDERE)
linkBuilder.authority(AUTHORITY_INTERACTIONS)
linkBuilder.appendPath(notificationType)
if (accountKey != null) {
linkBuilder.appendQueryParameter(QUERY_PARAM_ACCOUNT_KEY, accountKey.toString())
}
linkBuilder.appendQueryParameter(QUERY_PARAM_READ_POSITIONS, StringLongPair.toString(positions))
linkBuilder.appendQueryParameter(QUERY_PARAM_TIMESTAMP, System.currentTimeMillis().toString())
linkBuilder.appendQueryParameter(QUERY_PARAM_NOTIFICATION_TYPE, notificationType)
intent.data = linkBuilder.build()
return PendingIntent.getBroadcast(context, 0, intent, 0)
}
private fun getMarkReadDeleteIntent(context: Context, @NotificationType type: String, private fun getMarkReadDeleteIntent(context: Context, @NotificationType type: String,
accountKey: UserKey?, position: Long, accountKey: UserKey?, position: Long,
extraUserFollowing: Boolean): PendingIntent { extraUserFollowing: Boolean): PendingIntent {

View File

@ -675,7 +675,7 @@ object DataStoreUtils {
if (sortExpression != null) { if (sortExpression != null) {
builder.orderBy(sortExpression) builder.orderBy(sortExpression)
} }
resolver.rawQuery(builder.buildSQL(), bindingArgs).useCursor { cur -> resolver.rawQuery(builder.buildSQL(), bindingArgs)?.useCursor { cur ->
cur.moveToFirst() cur.moveToFirst()
val colIdx = creator.newIndex(cur) val colIdx = creator.newIndex(cur)
while (!cur.isAfterLast) { while (!cur.isAfterLast) {

View File

@ -26,8 +26,6 @@ import org.mariotaku.twidere.TwidereConstants.TIMELINE_POSITIONS_PREFERENCES_NAM
import org.mariotaku.twidere.annotation.CustomTabType import org.mariotaku.twidere.annotation.CustomTabType
import org.mariotaku.twidere.annotation.NotificationType import org.mariotaku.twidere.annotation.NotificationType
import org.mariotaku.twidere.annotation.ReadPositionTag import org.mariotaku.twidere.annotation.ReadPositionTag
import org.mariotaku.twidere.model.StringLongPair
import org.mariotaku.twidere.util.collection.CompactHashSet
class ReadStateManager(context: Context) { class ReadStateManager(context: Context) {
@ -39,24 +37,6 @@ class ReadStateManager(context: Context) {
return preferences.getLong(key, -1) return preferences.getLong(key, -1)
} }
fun getPositionPairs(key: String): Array<StringLongPair> {
if (TextUtils.isEmpty(key)) return emptyArray()
val set = preferences.getStringSet(key, null) ?: return emptyArray()
try {
return set.map { StringLongPair.valueOf(it) }.toTypedArray()
} catch (e: NumberFormatException) {
return emptyArray()
}
}
fun getPosition(key: String, keyId: String): Long {
if (TextUtils.isEmpty(key)) return -1
val set = preferences.getStringSet(key, null) ?: return -1
val prefix = keyId + ":"
val first = set.firstOrNull { it.startsWith(prefix) } ?: return -1
return StringLongPair.valueOf(first).value
}
fun registerOnSharedPreferenceChangeListener(listener: OnSharedPreferenceChangeListener) { fun registerOnSharedPreferenceChangeListener(listener: OnSharedPreferenceChangeListener) {
preferences.registerOnSharedPreferenceChangeListener(listener) preferences.registerOnSharedPreferenceChangeListener(listener)
} }
@ -65,46 +45,7 @@ class ReadStateManager(context: Context) {
preferences.unregisterOnSharedPreferenceChangeListener(listener) preferences.unregisterOnSharedPreferenceChangeListener(listener)
} }
fun setPosition(key: String, keyId: String, position: Long, acceptOlder: Boolean = false): Boolean { fun setPosition(key: String, position: Long, acceptOlder: Boolean = false): Boolean {
if (TextUtils.isEmpty(key)) return false
val set: MutableSet<String> = preferences.getStringSet(key, null) ?: CompactHashSet<String>()
val prefix = keyId + ":"
val keyValue: String? = set.firstOrNull { it.startsWith(prefix) }
val pair: StringLongPair
if (keyValue != null) {
// Found value
pair = StringLongPair.valueOf(keyValue)
if (!acceptOlder && pair.value > position) return false
set.remove(keyValue)
pair.value = position
} else {
pair = StringLongPair(keyId, position)
}
set.add(pair.toString())
val editor = preferences.edit()
editor.putStringSet(key, set)
editor.apply()
return true
}
fun setPositionPairs(key: String, pairs: Array<StringLongPair>?): Boolean {
if (TextUtils.isEmpty(key)) return false
val editor = preferences.edit()
if (pairs == null) {
editor.remove(key)
} else {
val set = CompactHashSet<String>()
for (pair in pairs) {
set.add(pair.toString())
}
editor.putStringSet(key, set)
}
editor.apply()
return true
}
@JvmOverloads fun setPosition(key: String, position: Long, acceptOlder: Boolean = false): Boolean {
if (TextUtils.isEmpty(key) || !acceptOlder && getPosition(key) >= position) return false if (TextUtils.isEmpty(key) || !acceptOlder && getPosition(key) >= position) return false
val editor = preferences.edit() val editor = preferences.edit()
editor.putLong(key, position) editor.putLong(key, position)
@ -112,10 +53,6 @@ class ReadStateManager(context: Context) {
return true return true
} }
interface OnReadStateChangeListener {
fun onReadStateChanged()
}
companion object { companion object {
@ReadPositionTag @ReadPositionTag

View File

@ -38,7 +38,7 @@ object FilterQueryBuilder {
val query = FilterQueryBuilder.isFilteredQuery(userKey, val query = FilterQueryBuilder.isFilteredQuery(userKey,
textPlain, quotedTextPlain, spans, quotedSpans, source, quotedSource, retweetedByKey, textPlain, quotedTextPlain, spans, quotedSpans, source, quotedSource, retweetedByKey,
quotedUserKey, true) quotedUserKey, true)
val cur = cr.rawQuery(query.first, query.second) val cur = cr.rawQuery(query.first, query.second) ?: return false
try { try {
return cur.moveToFirst() && cur.getInt(0) != 0 return cur.moveToFirst() && cur.getInt(0) != 0
} finally { } finally {