added messages unread count

This commit is contained in:
Mariotaku Lee 2017-03-13 13:35:11 +08:00
parent a89dbdf842
commit b767c28e75
No known key found for this signature in database
GPG Key ID: 15C10F89D7C33535
12 changed files with 96 additions and 59 deletions

View File

@ -73,6 +73,7 @@ public interface TwidereConstants extends SharedPreferenceConstants, IntentConst
String SCHEME_HTTPS = "https";
String SCHEME_CONTENT = ContentResolver.SCHEME_CONTENT;
String SCHEME_TWIDERE = "twidere";
String SCHEME_TWIDERE_SETTINGS = "twidere.settings";
String SCHEME_DATA = "data";
String PROTOCOL_HTTP = SCHEME_HTTP + "://";

View File

@ -176,7 +176,7 @@ dependencies {
compile 'com.github.mariotaku.MediaViewerLibrary:base:0.9.23'
compile 'com.github.mariotaku.MediaViewerLibrary:subsample-image-view:0.9.23'
compile 'com.github.mariotaku:SQLiteQB:0.9.10'
compile 'com.github.mariotaku:SQLiteQB:0.9.12'
compile "com.github.mariotaku.ObjectCursor:core:${libVersions['ObjectCursor']}"
compile 'com.github.mariotaku:MultiValueSwitch:0.9.7'
compile 'com.github.mariotaku:AbstractTask:0.9.4'

View File

@ -296,10 +296,17 @@
android:windowSoftInputMode="adjustResize">
<intent-filter>
<action android:name="android.intent.action.MANAGE_NETWORK_USAGE"/>
<action android:name="org.mariotaku.twidere.SETTINGS"/>
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<action android:name="org.mariotaku.twidere.SETTINGS"/>
<action android:name="org.mariotaku.twidere.VIEW"/>
<category android:name="android.intent.category.DEFAULT"/>
<data android:scheme="twidere.settings"/>
</intent-filter>
<meta-data
android:name="android.support.PARENT_ACTIVITY"

View File

@ -35,7 +35,6 @@ import android.graphics.Rect
import android.graphics.drawable.Drawable
import android.os.AsyncTask
import android.os.Bundle
import android.preference.PreferenceActivity
import android.support.annotation.StringRes
import android.support.v4.app.Fragment
import android.support.v4.app.NotificationCompat
@ -66,10 +65,9 @@ import org.mariotaku.abstask.library.TaskStarter
import org.mariotaku.chameleon.ChameleonUtils
import org.mariotaku.kpreferences.get
import org.mariotaku.kpreferences.set
import org.mariotaku.ktextension.addOnAccountsUpdatedListenerSafe
import org.mariotaku.ktextension.coerceInOr
import org.mariotaku.ktextension.contains
import org.mariotaku.ktextension.removeOnAccountsUpdatedListenerSafe
import org.mariotaku.ktextension.*
import org.mariotaku.sqliteqb.library.Columns
import org.mariotaku.sqliteqb.library.SQLFunctions
import org.mariotaku.twidere.Constants.*
import org.mariotaku.twidere.R
import org.mariotaku.twidere.activity.iface.IControlBarActivity.ControlBarShowHideHelper
@ -80,7 +78,6 @@ import org.mariotaku.twidere.constant.*
import org.mariotaku.twidere.extension.applyTheme
import org.mariotaku.twidere.fragment.AccountsDashboardFragment
import org.mariotaku.twidere.fragment.BaseDialogFragment
import org.mariotaku.twidere.fragment.CustomTabsFragment
import org.mariotaku.twidere.fragment.iface.IFloatingActionButtonFragment
import org.mariotaku.twidere.fragment.iface.RefreshScrollTopInterface
import org.mariotaku.twidere.fragment.iface.SupportFragmentCallback
@ -90,8 +87,10 @@ import org.mariotaku.twidere.model.SupportTabSpec
import org.mariotaku.twidere.model.Tab
import org.mariotaku.twidere.model.UserKey
import org.mariotaku.twidere.model.event.UnreadCountUpdatedEvent
import org.mariotaku.twidere.provider.TwidereDataStore
import org.mariotaku.twidere.provider.TwidereDataStore.Activities
import org.mariotaku.twidere.provider.TwidereDataStore.Statuses
import org.mariotaku.twidere.provider.TwidereDataStore.Messages.Conversations
import org.mariotaku.twidere.service.StreamingService
import org.mariotaku.twidere.util.*
import org.mariotaku.twidere.util.KeyboardShortcutsHandler.KeyboardShortcutCallback
@ -342,10 +341,7 @@ class HomeActivity : BaseActivity(), OnClickListener, OnPageChangeListener, Supp
triggerActionsClick()
}
emptyTabHint -> {
val intent = Intent(this, SettingsActivity::class.java)
intent.putExtra(PreferenceActivity.EXTRA_SHOW_FRAGMENT, CustomTabsFragment::class.java.name)
intent.putExtra(PreferenceActivity.EXTRA_SHOW_FRAGMENT_TITLE, R.string.tabs)
startActivityForResult(intent, REQUEST_SETTINGS)
startActivityForResult(IntentUtils.settings("tabs"), REQUEST_SETTINGS)
}
drawerToggleButton -> {
if (homeMenu.isDrawerOpen(GravityCompat.START) || homeMenu.isDrawerOpen(GravityCompat.END)) {
@ -933,6 +929,18 @@ class HomeActivity : BaseActivity(), OnClickListener, OnPageChangeListener, Supp
publishProgress(TabBadge(i, count))
result.put(i, count)
}
CustomTabType.DIRECT_MESSAGES -> {
val accountKeys = Utils.getAccountKeys(context, spec.args) ?: activatedKeys
val projection = (Conversations.COLUMNS + Conversations.UNREAD_COUNT).map {
TwidereQueryBuilder.mapConversationsProjection(it)
}.toTypedArray()
val count = context.contentResolver.getUnreadMessagesEntriesCursor(projection,
accountKeys)?.useCursor { cur ->
return@useCursor cur.count
} ?: -1
publishProgress(TabBadge(i, count))
result.put(i, count)
}
else -> {
publishProgress(TabBadge(i, -1))
}

View File

@ -32,7 +32,6 @@ import android.support.v7.app.AlertDialog
import android.support.v7.preference.Preference
import android.support.v7.preference.PreferenceFragmentCompat
import android.support.v7.preference.PreferenceFragmentCompat.OnPreferenceStartFragmentCallback
import android.text.TextUtils
import android.view.KeyEvent
import android.view.LayoutInflater
import android.view.View
@ -90,7 +89,7 @@ class SettingsActivity : BaseActivity(), OnItemClickListener, OnPreferenceStartF
entriesList.onItemClickListener = this
if (savedInstanceState == null) {
val initialTag = intent.getStringExtra(EXTRA_INITIAL_TAG)
val initialTag = intent.data?.path
var initialItem = -1
var firstEntry = -1
for (i in 0 until entriesAdapter.count) {
@ -99,7 +98,7 @@ class SettingsActivity : BaseActivity(), OnItemClickListener, OnPreferenceStartF
if (firstEntry == -1) {
firstEntry = i
}
if (TextUtils.equals(initialTag, entry.tag)) {
if (initialTag == entry.tag) {
initialItem = i
break
}
@ -415,8 +414,6 @@ class SettingsActivity : BaseActivity(), OnItemClickListener, OnPreferenceStartF
companion object {
val EXTRA_INITIAL_TAG = "initial_tag"
private val RESULT_SETTINGS_CHANGED = 10
fun setShouldRecreate(activity: Activity) {

View File

@ -254,8 +254,7 @@ class SignInActivity : BaseActivity(), OnClickListener, TextWatcher, APIEditorDi
R.id.settings -> {
if (signInTask != null && signInTask!!.status == AsyncTask.Status.RUNNING)
return false
val intent = Intent(this, SettingsActivity::class.java)
startActivity(intent)
startActivity(IntentUtils.settings())
}
R.id.edit_api -> {
if (signInTask != null && signInTask!!.status == AsyncTask.Status.RUNNING)

View File

@ -581,7 +581,7 @@ class AccountsDashboardFragment : BaseFragment(), LoaderCallbacks<AccountsInfo>,
closeAccountsDrawer()
}
R.id.settings -> {
val intent = Intent(activity, SettingsActivity::class.java)
val intent = IntentUtils.settings()
intent.addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT)
startActivityForResult(intent, REQUEST_SETTINGS)
closeAccountsDrawer()

View File

@ -23,7 +23,6 @@ import org.mariotaku.microblog.library.twitter.model.DirectMessage
import org.mariotaku.microblog.library.twitter.model.Status
import org.mariotaku.twidere.R
import org.mariotaku.twidere.TwidereConstants.LOGTAG
import org.mariotaku.twidere.activity.SettingsActivity
import org.mariotaku.twidere.annotation.AccountType
import org.mariotaku.twidere.constant.streamingNonMeteredNetworkKey
import org.mariotaku.twidere.constant.streamingPowerSavingKey
@ -40,6 +39,7 @@ import org.mariotaku.twidere.task.twitter.GetActivitiesAboutMeTask
import org.mariotaku.twidere.task.twitter.message.GetMessagesTask
import org.mariotaku.twidere.util.DataStoreUtils
import org.mariotaku.twidere.util.DebugLog
import org.mariotaku.twidere.util.IntentUtils
import org.mariotaku.twidere.util.Utils
import org.mariotaku.twidere.util.dagger.GeneralComponentHelper
import org.mariotaku.twidere.util.streaming.TwitterTimelineStreamCallback
@ -140,7 +140,7 @@ class StreamingService : BaseService() {
}
private fun showNotification() {
val intent = Intent(this, SettingsActivity::class.java)
val intent = IntentUtils.settings("streaming")
val contentIntent = PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT)
val contentTitle = getString(R.string.app_name)
val contentText = getString(R.string.timeline_streaming_running)

View File

@ -48,8 +48,9 @@ import org.mariotaku.twidere.extension.model.notificationDisabled
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.Messages.Conversations
import org.mariotaku.twidere.provider.TwidereDataStore.Statuses
import org.mariotaku.twidere.receiver.NotificationReceiver
import org.mariotaku.twidere.util.database.FilterQueryBuilder
import org.oshkimaadziig.george.androidutils.SpanFormatter
@ -257,30 +258,14 @@ class ContentNotificationManager(
val projection = (Conversations.COLUMNS + Conversations.UNREAD_COUNT).map {
TwidereQueryBuilder.mapConversationsProjection(it)
}.toTypedArray()
val qb = SQLQueryBuilder.select(Columns(*projection))
qb.from(Table(Conversations.TABLE_NAME))
qb.join(Join(false, Join.Operation.LEFT_OUTER, Table(Messages.TABLE_NAME),
Expression.equals(
Column(Table(Conversations.TABLE_NAME), Conversations.CONVERSATION_ID),
Column(Table(Messages.TABLE_NAME), Messages.CONVERSATION_ID)
)
))
qb.where(Expression.and(
Expression.equalsArgs(Column(Table(Conversations.TABLE_NAME), Conversations.ACCOUNT_KEY)),
Expression.lesserThan(Column(Table(Conversations.TABLE_NAME), Conversations.LAST_READ_TIMESTAMP),
Column(Table(Conversations.TABLE_NAME), Conversations.LOCAL_TIMESTAMP))
))
qb.groupBy(Column(Table(Messages.TABLE_NAME), Messages.CONVERSATION_ID))
qb.orderBy(OrderBy(arrayOf(Conversations.LOCAL_TIMESTAMP, Conversations.SORT_ID), booleanArrayOf(false, false)))
val selectionArgs = arrayOf(accountKey.toString())
val cur = cr.rawQuery(qb.buildSQL(), selectionArgs) ?: return
val cur = cr.getUnreadMessagesEntriesCursor(projection, arrayOf(accountKey)) ?: return
try {
if (cur.isEmpty) return
val indices = ObjectCursor.indicesFrom(cur, ParcelableMessageConversation::class.java)
var messageSum: Int = 0
cur.forEachRow { cur, pos ->
cur.forEachRow { cur, _ ->
messageSum += cur.getInt(indices[Conversations.UNREAD_COUNT])
return@forEachRow true
}
@ -321,6 +306,7 @@ class ContentNotificationManager(
}
}
/**
* @param limit -1 for no limit
* @return Remaining count, -1 if no rows present

View File

@ -5,21 +5,26 @@ import android.content.ContentResolver
import android.content.ContentValues
import android.content.Context
import android.content.SharedPreferences
import android.database.Cursor
import android.net.Uri
import android.support.annotation.WorkerThread
import android.support.v4.util.LongSparseArray
import org.mariotaku.kpreferences.get
import org.mariotaku.ktextension.toStringArray
import org.mariotaku.ktextension.useCursor
import org.mariotaku.library.objectcursor.ObjectCursor
import org.mariotaku.sqliteqb.library.*
import org.mariotaku.sqliteqb.library.Columns.Column
import org.mariotaku.twidere.constant.filterPossibilitySensitiveStatusesKey
import org.mariotaku.twidere.constant.filterUnavailableQuoteStatusesKey
import org.mariotaku.twidere.extension.rawQuery
import org.mariotaku.twidere.model.Draft
import org.mariotaku.twidere.model.ParcelableActivity
import org.mariotaku.twidere.model.ParcelableStatus
import org.mariotaku.twidere.model.ParcelableStatus.FilterFlags
import org.mariotaku.twidere.model.UserKey
import org.mariotaku.twidere.provider.TwidereDataStore.*
import org.mariotaku.twidere.provider.TwidereDataStore.Messages.Conversations
import org.mariotaku.twidere.util.DataStoreUtils.ACTIVITIES_URIS
import java.io.IOException
@ -31,43 +36,43 @@ fun buildStatusFilterWhereClause(preferences: SharedPreferences,
table: String,
extraSelection: Expression?): Expression {
val filteredUsersQuery = SQLQueryBuilder
.select(Columns.Column(Table(Filters.Users.TABLE_NAME), Filters.Users.USER_KEY))
.select(Column(Table(Filters.Users.TABLE_NAME), Filters.Users.USER_KEY))
.from(Tables(Filters.Users.TABLE_NAME))
.build()
val filteredUsersWhere = Expression.or(
Expression.`in`(Columns.Column(Table(table), Statuses.USER_KEY), filteredUsersQuery),
Expression.`in`(Columns.Column(Table(table), Statuses.RETWEETED_BY_USER_KEY), filteredUsersQuery),
Expression.`in`(Columns.Column(Table(table), Statuses.QUOTED_USER_KEY), filteredUsersQuery)
Expression.`in`(Column(Table(table), Statuses.USER_KEY), filteredUsersQuery),
Expression.`in`(Column(Table(table), Statuses.RETWEETED_BY_USER_KEY), filteredUsersQuery),
Expression.`in`(Column(Table(table), Statuses.QUOTED_USER_KEY), filteredUsersQuery)
)
val filteredIdsQueryBuilder = SQLQueryBuilder
.select(Columns.Column(Table(table), Statuses._ID))
.select(Column(Table(table), Statuses._ID))
.from(Tables(table))
.where(filteredUsersWhere)
.union()
.select(Columns(Columns.Column(Table(table), Statuses._ID)))
.select(Columns(Column(Table(table), Statuses._ID)))
.from(Tables(table, Filters.Sources.TABLE_NAME))
.where(Expression.or(
Expression.likeRaw(Columns.Column(Table(table), Statuses.SOURCE),
Expression.likeRaw(Column(Table(table), Statuses.SOURCE),
"'%>'||" + Filters.Sources.TABLE_NAME + "." + Filters.Sources.VALUE + "||'</a>%'"),
Expression.likeRaw(Columns.Column(Table(table), Statuses.QUOTED_SOURCE),
Expression.likeRaw(Column(Table(table), Statuses.QUOTED_SOURCE),
"'%>'||" + Filters.Sources.TABLE_NAME + "." + Filters.Sources.VALUE + "||'</a>%'")
))
.union()
.select(Columns(Columns.Column(Table(table), Statuses._ID)))
.select(Columns(Column(Table(table), Statuses._ID)))
.from(Tables(table, Filters.Keywords.TABLE_NAME))
.where(Expression.or(
Expression.likeRaw(Columns.Column(Table(table), Statuses.TEXT_PLAIN),
Expression.likeRaw(Column(Table(table), Statuses.TEXT_PLAIN),
"'%'||" + Filters.Keywords.TABLE_NAME + "." + Filters.Keywords.VALUE + "||'%'"),
Expression.likeRaw(Columns.Column(Table(table), Statuses.QUOTED_TEXT_PLAIN),
Expression.likeRaw(Column(Table(table), Statuses.QUOTED_TEXT_PLAIN),
"'%'||" + Filters.Keywords.TABLE_NAME + "." + Filters.Keywords.VALUE + "||'%'")
))
.union()
.select(Columns(Columns.Column(Table(table), Statuses._ID)))
.select(Columns(Column(Table(table), Statuses._ID)))
.from(Tables(table, Filters.Links.TABLE_NAME))
.where(Expression.or(
Expression.likeRaw(Columns.Column(Table(table), Statuses.SPANS),
Expression.likeRaw(Column(Table(table), Statuses.SPANS),
"'%'||" + Filters.Links.TABLE_NAME + "." + Filters.Links.VALUE + "||'%'"),
Expression.likeRaw(Columns.Column(Table(table), Statuses.QUOTED_SPANS),
Expression.likeRaw(Column(Table(table), Statuses.QUOTED_SPANS),
"'%'||" + Filters.Links.TABLE_NAME + "." + Filters.Links.VALUE + "||'%'")
))
var filterFlags: Long = 0
@ -81,9 +86,9 @@ fun buildStatusFilterWhereClause(preferences: SharedPreferences,
val filterExpression = Expression.or(
Expression.and(
Expression("(" + Statuses.FILTER_FLAGS + " & " + filterFlags + ") == 0"),
Expression.notIn(Columns.Column(Table(table), Statuses._ID), filteredIdsQueryBuilder.build())
Expression.notIn(Column(Table(table), Statuses._ID), filteredIdsQueryBuilder.build())
),
Expression.equals(Columns.Column(Table(table), Statuses.IS_GAP), 1)
Expression.equals(Column(Table(table), Statuses.IS_GAP), 1)
)
if (extraSelection != null) {
return Expression.and(filterExpression, extraSelection)
@ -133,14 +138,14 @@ fun deleteActivityStatus(cr: ContentResolver, accountKey: UserKey,
val updateWhereArgs: Array<String>
if (host != null) {
deleteWhere = Expression.and(
Expression.likeRaw(Columns.Column(Activities.ACCOUNT_KEY), "'%@'||?"),
Expression.likeRaw(Column(Activities.ACCOUNT_KEY), "'%@'||?"),
Expression.or(
Expression.equalsArgs(Activities.STATUS_ID),
Expression.equalsArgs(Activities.STATUS_RETWEET_ID)
)).sql
deleteWhereArgs = arrayOf(host, statusId, statusId)
updateWhere = Expression.and(
Expression.likeRaw(Columns.Column(Activities.ACCOUNT_KEY), "'%@'||?"),
Expression.likeRaw(Column(Activities.ACCOUNT_KEY), "'%@'||?"),
Expression.equalsArgs(Activities.STATUS_MY_RETWEET_ID)
).sql
updateWhereArgs = arrayOf(host, statusId)
@ -219,3 +224,25 @@ fun updateActivity(cr: ContentResolver, uri: Uri,
cr.update(uri, values.valueAt(i), updateWhere, updateWhereArgs)
}
}
fun ContentResolver.getUnreadMessagesEntriesCursor(projection: Array<Columns.Column>, accountKeys: Array<UserKey>): Cursor? {
val qb = SQLQueryBuilder.select(Columns(*projection))
qb.from(Table(Conversations.TABLE_NAME))
qb.join(Join(false, Join.Operation.LEFT_OUTER, Table(Messages.TABLE_NAME),
Expression.equals(
Column(Table(Conversations.TABLE_NAME), Conversations.CONVERSATION_ID),
Column(Table(Messages.TABLE_NAME), Messages.CONVERSATION_ID)
)
))
qb.where(Expression.and(
Expression.inArgs(Column(Table(Conversations.TABLE_NAME), Conversations.ACCOUNT_KEY),
accountKeys.size),
Expression.lesserThan(Column(Table(Conversations.TABLE_NAME), Conversations.LAST_READ_TIMESTAMP),
Column(Table(Conversations.TABLE_NAME), Conversations.LOCAL_TIMESTAMP))
))
qb.groupBy(Column(Table(Messages.TABLE_NAME), Messages.CONVERSATION_ID))
qb.orderBy(OrderBy(arrayOf(Column(Table(Conversations.TABLE_NAME), Conversations.LOCAL_TIMESTAMP),
Column(Table(Conversations.TABLE_NAME), Conversations.SORT_ID)), booleanArrayOf(false, false)))
val selectionArgs = accountKeys.toStringArray()
return rawQuery(qb.buildSQL(), selectionArgs)
}

View File

@ -912,4 +912,6 @@ object DataStoreUtils {
return null
}
}

View File

@ -634,6 +634,16 @@ object IntentUtils {
context.startActivity(intent)
}
fun settings(initialTag: String? = null): Intent {
val intent = Intent()
val builder = Uri.Builder()
builder.scheme(SCHEME_TWIDERE_SETTINGS)
builder.authority(initialTag.orEmpty())
intent.data = builder.build()
intent.`package` = BuildConfig.APPLICATION_ID
return intent
}
fun openProfileEditor(context: Context, accountId: UserKey?) {
val intent = Intent()
val builder = Uri.Builder()