diff --git a/twidere.component.common/src/main/java/org/mariotaku/twidere/TwidereConstants.java b/twidere.component.common/src/main/java/org/mariotaku/twidere/TwidereConstants.java index eb56565f3..2a48921b3 100644 --- a/twidere.component.common/src/main/java/org/mariotaku/twidere/TwidereConstants.java +++ b/twidere.component.common/src/main/java/org/mariotaku/twidere/TwidereConstants.java @@ -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 + "://"; diff --git a/twidere/build.gradle b/twidere/build.gradle index 0c6604f64..7d8fd39c1 100644 --- a/twidere/build.gradle +++ b/twidere/build.gradle @@ -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' diff --git a/twidere/src/main/AndroidManifest.xml b/twidere/src/main/AndroidManifest.xml index e17c98c5d..7d58c7195 100644 --- a/twidere/src/main/AndroidManifest.xml +++ b/twidere/src/main/AndroidManifest.xml @@ -296,10 +296,17 @@ android:windowSoftInputMode="adjustResize"> - + + + + + + + + { - 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)) } diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/activity/SettingsActivity.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/activity/SettingsActivity.kt index 867559076..ccd9f7421 100644 --- a/twidere/src/main/kotlin/org/mariotaku/twidere/activity/SettingsActivity.kt +++ b/twidere/src/main/kotlin/org/mariotaku/twidere/activity/SettingsActivity.kt @@ -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) { diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/activity/SignInActivity.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/activity/SignInActivity.kt index abde1ac1b..41e170635 100644 --- a/twidere/src/main/kotlin/org/mariotaku/twidere/activity/SignInActivity.kt +++ b/twidere/src/main/kotlin/org/mariotaku/twidere/activity/SignInActivity.kt @@ -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) diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/AccountsDashboardFragment.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/AccountsDashboardFragment.kt index 47ec06e1a..7275e4b1a 100644 --- a/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/AccountsDashboardFragment.kt +++ b/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/AccountsDashboardFragment.kt @@ -581,7 +581,7 @@ class AccountsDashboardFragment : BaseFragment(), LoaderCallbacks, 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() diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/service/StreamingService.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/service/StreamingService.kt index b6a94b27d..4b7069830 100644 --- a/twidere/src/main/kotlin/org/mariotaku/twidere/service/StreamingService.kt +++ b/twidere/src/main/kotlin/org/mariotaku/twidere/service/StreamingService.kt @@ -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) diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/util/ContentNotificationManager.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/util/ContentNotificationManager.kt index bc715cd9e..862322233 100644 --- a/twidere/src/main/kotlin/org/mariotaku/twidere/util/ContentNotificationManager.kt +++ b/twidere/src/main/kotlin/org/mariotaku/twidere/util/ContentNotificationManager.kt @@ -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 diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/util/DataStoreFunctions.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/util/DataStoreFunctions.kt index 4d1440428..44bc048b3 100644 --- a/twidere/src/main/kotlin/org/mariotaku/twidere/util/DataStoreFunctions.kt +++ b/twidere/src/main/kotlin/org/mariotaku/twidere/util/DataStoreFunctions.kt @@ -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 + "||'%'"), - Expression.likeRaw(Columns.Column(Table(table), Statuses.QUOTED_SOURCE), + Expression.likeRaw(Column(Table(table), Statuses.QUOTED_SOURCE), "'%>'||" + Filters.Sources.TABLE_NAME + "." + Filters.Sources.VALUE + "||'%'") )) .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 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, accountKeys: Array): 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) +} \ No newline at end of file diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/util/DataStoreUtils.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/util/DataStoreUtils.kt index addf17ca2..02eeaa66e 100644 --- a/twidere/src/main/kotlin/org/mariotaku/twidere/util/DataStoreUtils.kt +++ b/twidere/src/main/kotlin/org/mariotaku/twidere/util/DataStoreUtils.kt @@ -912,4 +912,6 @@ object DataStoreUtils { return null } + + } diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/util/IntentUtils.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/util/IntentUtils.kt index 13730b5cd..674cdcfe9 100644 --- a/twidere/src/main/kotlin/org/mariotaku/twidere/util/IntentUtils.kt +++ b/twidere/src/main/kotlin/org/mariotaku/twidere/util/IntentUtils.kt @@ -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()