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);
}
/**
* 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) {
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_CONVERSATION_ID = "conversation_id";
String QUERY_PARAM_READ_POSITION = "param_read_position";
String QUERY_PARAM_READ_POSITIONS = "param_read_positions";
String QUERY_PARAM_LIMIT = "limit";
String QUERY_PARAM_EXTRA = "extra";
String QUERY_PARAM_TIMESTAMP = "timestamp";

View File

@ -142,6 +142,10 @@ public class ParcelableMessageConversation implements Parcelable {
@CursorField(value = Conversations.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
* yet.

View File

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

View File

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

View File

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

View File

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

View File

@ -64,23 +64,12 @@ class NotificationReceiver : BroadcastReceiver() {
}
val manager = holder.readStateManager
val paramReadPosition = uri.getQueryParameter(QUERY_PARAM_READ_POSITION)
val paramReadPositions = uri.getQueryParameter(QUERY_PARAM_READ_POSITIONS)
@ReadPositionTag
val tag = getPositionTag(notificationType)
if (tag != null && !TextUtils.isEmpty(paramReadPosition)) {
manager.setPosition(Utils.getReadPositionTagWithAccount(tag, accountKey),
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 org.mariotaku.ktextension.toInt
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.Paging
import org.mariotaku.microblog.library.twitter.model.User
import org.mariotaku.microblog.library.twitter.model.fixMedia
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.isOfficial
import org.mariotaku.twidere.extension.model.newMicroBlogInstance
import org.mariotaku.twidere.extension.model.timestamp
import org.mariotaku.twidere.model.*
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.util.AccountUtils
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.UserKeyUtils
import org.mariotaku.twidere.provider.TwidereDataStore.Messages
import org.mariotaku.twidere.provider.TwidereDataStore.Messages.Conversations
import org.mariotaku.twidere.task.BaseAbstractTask
import org.mariotaku.twidere.util.DataStoreUtils
import org.mariotaku.twidere.util.content.ContentResolverUtils
import java.util.*
@ -56,10 +64,10 @@ class GetMessagesTask(
val am = android.accounts.AccountManager.get(context)
accountKeys.forEachIndexed { i, accountKey ->
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 {
getMessages(microBlog, details, param, i)
} catch (e: org.mariotaku.microblog.library.MicroBlogException) {
} catch (e: MicroBlogException) {
return@forEachIndexed
}
Companion.storeMessages(context, messages, details)
@ -68,16 +76,16 @@ class GetMessagesTask(
override fun afterExecute(callback: ((Boolean) -> Unit)?, result: Unit) {
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) {
org.mariotaku.twidere.annotation.AccountType.FANFOU -> {
AccountType.FANFOU -> {
// Use fanfou DM api, disabled since it's conversation api is not suitable for paging
// return getFanfouMessages(microBlog, details, param, index)
}
org.mariotaku.twidere.annotation.AccountType.TWITTER -> {
AccountType.TWITTER -> {
// Use official DM api
if (details.isOfficial(context)) {
return getTwitterOfficialMessages(microBlog, details, param, index)
@ -88,7 +96,7 @@ class GetMessagesTask(
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 {
val conversationId = param.conversationId
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
if (conversationId == null) {
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 sinceIds = if (param.hasSinceIds) param.sinceIds 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)
val maxId = maxIds?.get(index)
val sinceId = sinceIds?.get(index)
@ -124,7 +132,7 @@ class GetMessagesTask(
sinceId(sinceId)
}
})
val sent = microBlog.getSentDirectMessages(org.mariotaku.microblog.library.twitter.model.Paging().apply {
val sent = microBlog.getSentDirectMessages(Paging().apply {
count(100)
val accountsCount = param.accountKeys.size
val maxId = maxIds?.get(accountsCount + index)
@ -143,22 +151,22 @@ class GetMessagesTask(
val conversationIds = hashSetOf<String>()
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 {
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)
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))
insertMessages.add(message)
conversations.addConversation(message.conversation_id, details, message, setOf(dm.sender, dm.recipient))
}
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))
insertMessages.add(message)
conversations.addConversation(message.conversation_id, details, message, setOf(dm.sender, dm.recipient))
@ -166,10 +174,10 @@ class GetMessagesTask(
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 {
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)
}
@ -178,14 +186,14 @@ class GetMessagesTask(
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 {
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) {
microBlog.getUserUpdates(cursor).userEvents
} else {
microBlog.getUserInbox(org.mariotaku.microblog.library.twitter.model.Paging().apply {
microBlog.getUserInbox(Paging().apply {
if (maxId != null) {
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 cursor = param.cursors?.get(index)
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)
if (page >= 0) {
page(page)
@ -214,7 +222,7 @@ class GetMessagesTask(
result.forEachIndexed { i, item ->
val dm = item.dm
// 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))
val mc = conversations.addConversation(message.conversation_id, details, message,
setOf(dm.sender, dm.recipient))
@ -237,9 +245,9 @@ class GetMessagesTask(
override val sinceIds: Array<String?>?
get() {
val incomingIds = org.mariotaku.twidere.util.DataStoreUtils.getNewestMessageIds(context, Messages.CONTENT_URI,
val incomingIds = DataStoreUtils.getNewestMessageIds(context, Messages.CONTENT_URI,
defaultKeys, false)
val outgoingIds = org.mariotaku.twidere.util.DataStoreUtils.getNewestMessageIds(context, Messages.CONTENT_URI,
val outgoingIds = DataStoreUtils.getNewestMessageIds(context, Messages.CONTENT_URI,
defaultKeys, true)
return incomingIds + outgoingIds
}
@ -247,7 +255,7 @@ class GetMessagesTask(
override val cursors: Array<String?>?
get() {
val cursors = arrayOfNulls<String>(defaultKeys.size)
val newestConversations = org.mariotaku.twidere.util.DataStoreUtils.getNewestConversations(context,
val newestConversations = DataStoreUtils.getNewestConversations(context,
Conversations.CONTENT_URI, twitterOfficialKeys)
newestConversations.forEachIndexed { i, conversation ->
cursors[i] = conversation?.request_cursor
@ -265,11 +273,11 @@ class GetMessagesTask(
) : RefreshMessagesTaskParam(context) {
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)
val outgoingIds = org.mariotaku.twidere.util.DataStoreUtils.getOldestMessageIds(context, Messages.CONTENT_URI,
val outgoingIds = DataStoreUtils.getOldestMessageIds(context, Messages.CONTENT_URI,
defaultKeys, true)
val oldestConversations = org.mariotaku.twidere.util.DataStoreUtils.getOldestConversations(context,
val oldestConversations = DataStoreUtils.getOldestConversations(context,
Conversations.CONTENT_URI, twitterOfficialKeys)
oldestConversations.forEachIndexed { i, conversation ->
val extras = conversation?.conversation_extras as? TwitterOfficialConversationExtras ?: return@forEachIndexed
@ -305,13 +313,13 @@ class GetMessagesTask(
var taskTag: String? = null
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 {
return@lazy accounts.map { account ->
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 account.key
@ -358,7 +366,7 @@ class GetMessagesTask(
return@mapNotNullTo null
}
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.request_cursor = response.cursor
conversation.conversation_extras_type = ParcelableMessageConversation.ExtrasType.TWITTER_OFFICIAL
conversation.last_read_id = v.lastReadEventId
conversation.conversation_extras = TwitterOfficialConversationExtras().apply {
this.minEntryId = v.minEntryId
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.provider.TwidereDataStore.Messages.Conversations
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.Companion.addConversation
import org.mariotaku.twidere.task.twitter.message.GetMessagesTask.Companion.addLocalConversations
import org.mariotaku.twidere.task.twitter.UpdateStatusTask
/**
* Created by mariotaku on 2017/2/8.
@ -88,7 +88,8 @@ class SendMessageTask(
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 deleteAlways: List<UpdateStatusTask.MediaDeletionItem>? = null
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.model.*
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.Statuses
import org.mariotaku.twidere.receiver.NotificationReceiver
@ -65,6 +64,81 @@ class ContentNotificationManager(
private var nameFirst: 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) {
val cr = context.contentResolver
val accountKey = pref.accountKey
@ -163,12 +237,9 @@ class ContentNotificationManager(
}
val notificationId = Utils.getNotificationId(NOTIFICATION_ID_INTERACTIONS_TIMELINE, accountKey)
notificationManager.notify("interactions", notificationId, builder.build())
Utils.sendPebbleNotification(context, context.resources.getString(R.string.interactions), pebbleNotificationStringBuilder.toString())
Utils.sendPebbleNotification(context, context.getString(R.string.interactions), pebbleNotificationStringBuilder.toString())
}
private fun applyNotificationPreferences(builder: NotificationCompat.Builder, pref: AccountPreferences, defaultFlags: Int) {
var notificationDefaults = 0
if (AccountPreferences.isNotificationHasLight(defaultFlags)) {
@ -195,80 +266,6 @@ class ContentNotificationManager(
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,
@NotificationType notificationType: String, accountKey: UserKey?, readPosition: Long): PendingIntent {
@ -302,26 +299,6 @@ class ContentNotificationManager(
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,
accountKey: UserKey?, position: Long,
extraUserFollowing: Boolean): PendingIntent {

View File

@ -675,7 +675,7 @@ object DataStoreUtils {
if (sortExpression != null) {
builder.orderBy(sortExpression)
}
resolver.rawQuery(builder.buildSQL(), bindingArgs).useCursor { cur ->
resolver.rawQuery(builder.buildSQL(), bindingArgs)?.useCursor { cur ->
cur.moveToFirst()
val colIdx = creator.newIndex(cur)
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.NotificationType
import org.mariotaku.twidere.annotation.ReadPositionTag
import org.mariotaku.twidere.model.StringLongPair
import org.mariotaku.twidere.util.collection.CompactHashSet
class ReadStateManager(context: Context) {
@ -39,24 +37,6 @@ class ReadStateManager(context: Context) {
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) {
preferences.registerOnSharedPreferenceChangeListener(listener)
}
@ -65,46 +45,7 @@ class ReadStateManager(context: Context) {
preferences.unregisterOnSharedPreferenceChangeListener(listener)
}
fun setPosition(key: String, keyId: 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 {
fun setPosition(key: String, position: Long, acceptOlder: Boolean = false): Boolean {
if (TextUtils.isEmpty(key) || !acceptOlder && getPosition(key) >= position) return false
val editor = preferences.edit()
editor.putLong(key, position)
@ -112,10 +53,6 @@ class ReadStateManager(context: Context) {
return true
}
interface OnReadStateChangeListener {
fun onReadStateChanged()
}
companion object {
@ReadPositionTag

View File

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