mirror of
https://github.com/TwidereProject/Twidere-Android
synced 2025-02-17 04:00:48 +01:00
fixed interactions notifications unread
improved streaming refresh
This commit is contained in:
parent
1b542cea6c
commit
9506fa704e
@ -9,7 +9,7 @@ import com.bluelinelabs.logansquare.annotation.JsonObject;
|
||||
@JsonObject
|
||||
public class DeletionEvent {
|
||||
|
||||
@JsonField(name = "id")
|
||||
@JsonField(name = {"id", "id_str"})
|
||||
String id;
|
||||
@JsonField(name = "user_id")
|
||||
String userId;
|
||||
|
@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Twidere - Twitter client for Android
|
||||
*
|
||||
* Copyright (C) 2012-2017 Mariotaku Lee <mariotaku.lee@gmail.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.mariotaku.twidere.model
|
||||
|
||||
import org.junit.Assert
|
||||
import org.junit.Test
|
||||
|
||||
/**
|
||||
* Created by mariotaku on 2017/3/13.
|
||||
*/
|
||||
class ItemCountsTest {
|
||||
@Test
|
||||
fun getItemCountIndex() {
|
||||
val counts = ItemCounts(3)
|
||||
counts[0] = 2
|
||||
counts[1] = 3
|
||||
counts[2] = 3
|
||||
|
||||
Assert.assertEquals(0, counts.getItemCountIndex(1))
|
||||
Assert.assertEquals(1, counts.getItemCountIndex(2))
|
||||
Assert.assertEquals(1, counts.getItemCountIndex(4))
|
||||
Assert.assertEquals(2, counts.getItemCountIndex(7))
|
||||
Assert.assertEquals(-1, counts.getItemCountIndex(10))
|
||||
Assert.assertEquals(-1, counts.getItemCountIndex(-1))
|
||||
}
|
||||
|
||||
}
|
@ -20,6 +20,7 @@ import android.content.Context;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.support.annotation.IntRange;
|
||||
import android.support.v4.view.ViewCompat;
|
||||
import android.support.v7.widget.LinearLayoutManager;
|
||||
import android.support.v7.widget.RecyclerView;
|
||||
@ -28,7 +29,7 @@ import android.support.v7.widget.RecyclerView.State;
|
||||
import android.support.v7.widget.TintTypedArray;
|
||||
import android.view.View;
|
||||
|
||||
public class DividerItemDecoration extends RecyclerView.ItemDecoration {
|
||||
public class ExtendedDividerItemDecoration extends RecyclerView.ItemDecoration {
|
||||
|
||||
private static final int[] ATTRS = new int[]{
|
||||
android.R.attr.listDivider
|
||||
@ -45,7 +46,8 @@ public class DividerItemDecoration extends RecyclerView.ItemDecoration {
|
||||
private Padding mPadding;
|
||||
private int mDecorationStart = -1, mDecorationEnd = -1, mDecorationEndOffset;
|
||||
|
||||
public DividerItemDecoration(Context context, int orientation) {
|
||||
@SuppressWarnings("RestrictedApi")
|
||||
public ExtendedDividerItemDecoration(Context context, int orientation) {
|
||||
final TintTypedArray a = TintTypedArray.obtainStyledAttributes(context, null, ATTRS);
|
||||
mDivider = a.getDrawable(0);
|
||||
a.recycle();
|
||||
@ -106,7 +108,7 @@ public class DividerItemDecoration extends RecyclerView.ItemDecoration {
|
||||
for (int i = 0; i < childCount; i++) {
|
||||
final View child = parent.getChildAt(i);
|
||||
final int childPos = parent.getChildAdapterPosition(child);
|
||||
if (!isDividerEnabled(childPos)) continue;
|
||||
if (childPos < 0 || !isDividerEnabled(childPos)) continue;
|
||||
final int start = getDecorationStart(), end = getDecorationEnd(parent);
|
||||
if (start >= 0 && childPos < start || end >= 0 && childPos > end) continue;
|
||||
final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child
|
||||
@ -137,8 +139,8 @@ public class DividerItemDecoration extends RecyclerView.ItemDecoration {
|
||||
for (int i = 0; i < childCount; i++) {
|
||||
final View child = parent.getChildAt(i);
|
||||
final int childPos = parent.getChildAdapterPosition(child);
|
||||
if (childPos < 0 || !isDividerEnabled(childPos)) continue;
|
||||
final int start = getDecorationStart(), end = getDecorationEnd(parent);
|
||||
if (!isDividerEnabled(childPos)) continue;
|
||||
if (start >= 0 && childPos < start || end >= 0 && childPos > end) continue;
|
||||
final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child
|
||||
.getLayoutParams();
|
||||
@ -156,7 +158,7 @@ public class DividerItemDecoration extends RecyclerView.ItemDecoration {
|
||||
public void getItemOffsets(Rect outRect, View view, RecyclerView parent, State state) {
|
||||
if (mDivider == null) return;
|
||||
final int childPos = parent.getChildAdapterPosition(view);
|
||||
if (!isDividerEnabled(childPos)) return;
|
||||
if (childPos < 0 || !isDividerEnabled(childPos)) return;
|
||||
final int start = getDecorationStart(), end = getDecorationEnd(parent);
|
||||
if (start >= 0 && childPos < start || end >= 0 && childPos > end) {
|
||||
outRect.setEmpty();
|
@ -31,7 +31,7 @@ import org.mariotaku.chameleon.Chameleon;
|
||||
import org.mariotaku.chameleon.ChameleonUtils;
|
||||
import org.mariotaku.chameleon.ChameleonView;
|
||||
import org.mariotaku.twidere.R;
|
||||
import org.mariotaku.twidere.adapter.decorator.DividerItemDecoration;
|
||||
import org.mariotaku.twidere.adapter.decorator.ExtendedDividerItemDecoration;
|
||||
import org.mariotaku.twidere.util.ThemeUtils;
|
||||
import org.mariotaku.twidere.view.iface.PagerIndicator;
|
||||
|
||||
@ -47,7 +47,7 @@ public class TabPagerIndicator extends RecyclerView implements PagerIndicator, C
|
||||
private final int mStripHeight;
|
||||
private final TabPagerIndicatorAdapter mIndicatorAdapter;
|
||||
private final TabLayoutManager mLayoutManager;
|
||||
private final DividerItemDecoration mItemDecoration;
|
||||
private final ExtendedDividerItemDecoration mItemDecoration;
|
||||
private ViewPager mViewPager;
|
||||
private PagerAdapter mPagerProvider;
|
||||
|
||||
@ -61,7 +61,7 @@ public class TabPagerIndicator extends RecyclerView implements PagerIndicator, C
|
||||
ViewCompat.setLayoutDirection(this, ViewCompat.LAYOUT_DIRECTION_LTR);
|
||||
final Resources res = getResources();
|
||||
mIndicatorAdapter = new TabPagerIndicatorAdapter(this);
|
||||
mItemDecoration = new DividerItemDecoration(context, HORIZONTAL);
|
||||
mItemDecoration = new ExtendedDividerItemDecoration(context, HORIZONTAL);
|
||||
mStripHeight = res.getDimensionPixelSize(R.dimen.element_spacing_small);
|
||||
setOverScrollMode(OVER_SCROLL_NEVER);
|
||||
setHorizontalScrollBarEnabled(false);
|
||||
|
@ -89,7 +89,7 @@ class SettingsActivity : BaseActivity(), OnItemClickListener, OnPreferenceStartF
|
||||
entriesList.onItemClickListener = this
|
||||
|
||||
if (savedInstanceState == null) {
|
||||
val initialTag = intent.data?.path
|
||||
val initialTag = intent.data?.authority
|
||||
var initialItem = -1
|
||||
var firstEntry = -1
|
||||
for (i in 0 until entriesAdapter.count) {
|
||||
|
@ -258,7 +258,7 @@ class ParcelableActivitiesAdapter(
|
||||
if (isGapItem(position)) {
|
||||
return ITEM_VIEW_TYPE_GAP
|
||||
}
|
||||
val activity = getActivity(position)
|
||||
val activity = getActivity(position, false)
|
||||
when (activity.action) {
|
||||
Activity.Action.MENTION -> {
|
||||
if (ArrayUtils.isEmpty(activity.target_object_statuses)) {
|
||||
|
@ -140,6 +140,7 @@ internal inline fun <T> configureLoadProfileImage(context: Context, @ImageShapeS
|
||||
): DrawableRequestBuilder<T> {
|
||||
val builder = create()
|
||||
builder.diskCacheStrategy(DiskCacheStrategy.RESULT)
|
||||
builder.centerCrop()
|
||||
builder.dontAnimate()
|
||||
when (shapeStyle) {
|
||||
ImageShapeStyle.SHAPE_CIRCLE -> {
|
||||
|
@ -48,7 +48,7 @@ import org.mariotaku.twidere.adapter.ParcelableActivitiesAdapter.Companion.ITEM_
|
||||
import org.mariotaku.twidere.adapter.ParcelableActivitiesAdapter.Companion.ITEM_VIEW_TYPE_STATUS
|
||||
import org.mariotaku.twidere.adapter.ParcelableActivitiesAdapter.Companion.ITEM_VIEW_TYPE_STUB
|
||||
import org.mariotaku.twidere.adapter.ParcelableActivitiesAdapter.Companion.ITEM_VIEW_TYPE_TITLE_SUMMARY
|
||||
import org.mariotaku.twidere.adapter.decorator.DividerItemDecoration
|
||||
import org.mariotaku.twidere.adapter.decorator.ExtendedDividerItemDecoration
|
||||
import org.mariotaku.twidere.adapter.iface.ILoadMoreSupportAdapter
|
||||
import org.mariotaku.twidere.annotation.ReadPositionTag
|
||||
import org.mariotaku.twidere.constant.IntentConstants.*
|
||||
@ -543,9 +543,10 @@ abstract class AbsActivitiesFragment protected constructor() :
|
||||
|
||||
override fun createItemDecoration(context: Context, recyclerView: RecyclerView,
|
||||
layoutManager: LinearLayoutManager): RecyclerView.ItemDecoration? {
|
||||
val itemDecoration = object : DividerItemDecoration(context,
|
||||
val itemDecoration = object : ExtendedDividerItemDecoration(context,
|
||||
(recyclerView.layoutManager as LinearLayoutManager).orientation) {
|
||||
override fun isDividerEnabled(childPos: Int): Boolean {
|
||||
if (childPos >= layoutManager.itemCount || childPos < 0) return false
|
||||
when (adapter.getItemViewType(childPos)) {
|
||||
ITEM_VIEW_TYPE_STATUS, ITEM_VIEW_TYPE_TITLE_SUMMARY, ITEM_VIEW_TYPE_GAP,
|
||||
ITEM_VIEW_TYPE_STUB -> {
|
||||
|
@ -26,7 +26,7 @@ import android.support.v7.widget.RecyclerView
|
||||
import org.mariotaku.ktextension.contains
|
||||
|
||||
import org.mariotaku.twidere.adapter.LoadMoreSupportAdapter
|
||||
import org.mariotaku.twidere.adapter.decorator.DividerItemDecoration
|
||||
import org.mariotaku.twidere.adapter.decorator.ExtendedDividerItemDecoration
|
||||
import org.mariotaku.twidere.adapter.iface.ILoadMoreSupportAdapter
|
||||
import org.mariotaku.twidere.adapter.iface.ILoadMoreSupportAdapter.IndicatorPosition
|
||||
|
||||
@ -39,12 +39,12 @@ abstract class AbsContentListRecyclerViewFragment<A : LoadMoreSupportAdapter<Rec
|
||||
|
||||
override fun createItemDecoration(context: Context, recyclerView: RecyclerView,
|
||||
layoutManager: LinearLayoutManager): RecyclerView.ItemDecoration? {
|
||||
return DividerItemDecoration(context, layoutManager.orientation)
|
||||
return ExtendedDividerItemDecoration(context, layoutManager.orientation)
|
||||
}
|
||||
|
||||
override fun setLoadMoreIndicatorPosition(@IndicatorPosition position: Long) {
|
||||
val decor = itemDecoration
|
||||
if (decor is DividerItemDecoration) {
|
||||
if (decor is ExtendedDividerItemDecoration) {
|
||||
decor.setDecorationStart(if (ILoadMoreSupportAdapter.START in position) 1 else 0)
|
||||
decor.setDecorationEndOffset(if (ILoadMoreSupportAdapter.END in position) 1 else 0)
|
||||
}
|
||||
|
@ -44,7 +44,7 @@ import org.mariotaku.sqliteqb.library.Expression
|
||||
import org.mariotaku.twidere.R
|
||||
import org.mariotaku.twidere.activity.AccountSelectorActivity
|
||||
import org.mariotaku.twidere.adapter.ParcelableStatusesAdapter
|
||||
import org.mariotaku.twidere.adapter.decorator.DividerItemDecoration
|
||||
import org.mariotaku.twidere.adapter.decorator.ExtendedDividerItemDecoration
|
||||
import org.mariotaku.twidere.adapter.iface.ILoadMoreSupportAdapter
|
||||
import org.mariotaku.twidere.annotation.ReadPositionTag
|
||||
import org.mariotaku.twidere.annotation.Referral
|
||||
@ -402,7 +402,7 @@ abstract class AbsStatusesFragment : AbsContentListRecyclerViewFragment<Parcelab
|
||||
}
|
||||
|
||||
override fun createItemDecoration(context: Context, recyclerView: RecyclerView, layoutManager: LinearLayoutManager): RecyclerView.ItemDecoration? {
|
||||
val itemDecoration = DividerItemDecoration(context, (recyclerView.layoutManager as LinearLayoutManager).orientation)
|
||||
val itemDecoration = ExtendedDividerItemDecoration(context, (recyclerView.layoutManager as LinearLayoutManager).orientation)
|
||||
val res = context.resources
|
||||
if (adapter.profileImageEnabled) {
|
||||
val decorPaddingLeft = res.getDimensionPixelSize(R.dimen.element_spacing_normal) * 2 + res.getDimensionPixelSize(R.dimen.icon_size_status_profile_image)
|
||||
|
@ -175,14 +175,12 @@ abstract class CursorActivitiesFragment : AbsActivitiesFragment() {
|
||||
}
|
||||
|
||||
override val sinceIds: Array<String?>?
|
||||
get() = getNewestActivityIds(accountKeys)
|
||||
get() = DataStoreUtils.getNewestActivityMaxPositions(context, contentUri,
|
||||
accountKeys.toNulls())
|
||||
|
||||
override val sinceSortIds: LongArray?
|
||||
get() {
|
||||
val context = context ?: return null
|
||||
return DataStoreUtils.getNewestActivityMaxSortPositions(context,
|
||||
contentUri, accountKeys.toNulls())
|
||||
}
|
||||
get() = DataStoreUtils.getNewestActivityMaxSortPositions(context, contentUri,
|
||||
accountKeys.toNulls())
|
||||
|
||||
override val hasSinceIds: Boolean
|
||||
get() = true
|
||||
@ -198,11 +196,6 @@ abstract class CursorActivitiesFragment : AbsActivitiesFragment() {
|
||||
return DataStoreUtils.buildActivityFilterWhereClause(table, null)
|
||||
}
|
||||
|
||||
protected fun getNewestActivityIds(accountKeys: Array<UserKey>): Array<String?>? {
|
||||
val context = context ?: return null
|
||||
return DataStoreUtils.getNewestActivityMaxPositions(context, contentUri, accountKeys.toNulls())
|
||||
}
|
||||
|
||||
protected abstract val notificationType: Int
|
||||
|
||||
override fun setUserVisibleHint(isVisibleToUser: Boolean) {
|
||||
|
@ -34,7 +34,7 @@ import org.mariotaku.commons.parcel.ParcelUtils
|
||||
import org.mariotaku.kpreferences.get
|
||||
import org.mariotaku.twidere.R
|
||||
import org.mariotaku.twidere.adapter.ParcelableUsersAdapter
|
||||
import org.mariotaku.twidere.adapter.decorator.DividerItemDecoration
|
||||
import org.mariotaku.twidere.adapter.decorator.ExtendedDividerItemDecoration
|
||||
import org.mariotaku.twidere.adapter.iface.ILoadMoreSupportAdapter
|
||||
import org.mariotaku.twidere.adapter.iface.IUsersAdapter
|
||||
import org.mariotaku.twidere.adapter.iface.IUsersAdapter.UserClickListener
|
||||
@ -187,7 +187,7 @@ abstract class ParcelableUsersFragment : AbsContentListRecyclerViewFragment<Parc
|
||||
|
||||
override fun createItemDecoration(context: Context, recyclerView: RecyclerView,
|
||||
layoutManager: LinearLayoutManager): RecyclerView.ItemDecoration? {
|
||||
val itemDecoration = DividerItemDecoration(context,
|
||||
val itemDecoration = ExtendedDividerItemDecoration(context,
|
||||
(recyclerView.layoutManager as LinearLayoutManager).orientation)
|
||||
val res = context.resources
|
||||
if (adapter.profileImageEnabled) {
|
||||
|
@ -83,7 +83,7 @@ import org.mariotaku.twidere.activity.ColorPickerDialogActivity
|
||||
import org.mariotaku.twidere.adapter.BaseRecyclerViewAdapter
|
||||
import org.mariotaku.twidere.adapter.ListParcelableStatusesAdapter
|
||||
import org.mariotaku.twidere.adapter.LoadMoreSupportAdapter
|
||||
import org.mariotaku.twidere.adapter.decorator.DividerItemDecoration
|
||||
import org.mariotaku.twidere.adapter.decorator.ExtendedDividerItemDecoration
|
||||
import org.mariotaku.twidere.adapter.iface.IGapSupportedAdapter
|
||||
import org.mariotaku.twidere.adapter.iface.IItemCountsAdapter
|
||||
import org.mariotaku.twidere.adapter.iface.ILoadMoreSupportAdapter
|
||||
@ -131,7 +131,7 @@ import java.util.*
|
||||
class StatusFragment : BaseFragment(), LoaderCallbacks<SingleResponse<ParcelableStatus>>,
|
||||
OnMediaClickListener, StatusClickListener, KeyboardShortcutCallback,
|
||||
ContentListSupport<StatusFragment.StatusAdapter> {
|
||||
private var mItemDecoration: DividerItemDecoration? = null
|
||||
private var mItemDecoration: ExtendedDividerItemDecoration? = null
|
||||
|
||||
override lateinit var adapter: StatusAdapter
|
||||
|
||||
@ -2187,7 +2187,7 @@ class StatusFragment : BaseFragment(), LoaderCallbacks<SingleResponse<Parcelable
|
||||
context: Context,
|
||||
private val statusAdapter: StatusAdapter,
|
||||
orientation: Int
|
||||
) : DividerItemDecoration(context, orientation) {
|
||||
) : ExtendedDividerItemDecoration(context, orientation) {
|
||||
|
||||
override fun isDividerEnabled(childPos: Int): Boolean {
|
||||
if (childPos >= statusAdapter.itemCount || childPos < 0) return false
|
||||
|
@ -66,9 +66,6 @@ class AccountPreferences(private val context: Context, val accountKey: UserKey)
|
||||
}
|
||||
}
|
||||
|
||||
val isAutoRefreshDirectMessagesEnabled: Boolean
|
||||
get() = preferences.getBoolean(KEY_AUTO_REFRESH_DIRECT_MESSAGES, DEFAULT_AUTO_REFRESH_DIRECT_MESSAGES)
|
||||
|
||||
val isAutoRefreshEnabled: Boolean
|
||||
get() = preferences.getBoolean(KEY_AUTO_REFRESH, preferences.getBoolean(KEY_DEFAULT_AUTO_REFRESH, false))
|
||||
|
||||
@ -78,12 +75,24 @@ class AccountPreferences(private val context: Context, val accountKey: UserKey)
|
||||
val isAutoRefreshMentionsEnabled: Boolean
|
||||
get() = preferences.getBoolean(KEY_AUTO_REFRESH_MENTIONS, DEFAULT_AUTO_REFRESH_MENTIONS)
|
||||
|
||||
val isAutoRefreshDirectMessagesEnabled: Boolean
|
||||
get() = preferences.getBoolean(KEY_AUTO_REFRESH_DIRECT_MESSAGES, DEFAULT_AUTO_REFRESH_DIRECT_MESSAGES)
|
||||
|
||||
val isAutoRefreshTrendsEnabled: Boolean
|
||||
get() = preferences.getBoolean(KEY_AUTO_REFRESH_TRENDS, DEFAULT_AUTO_REFRESH_TRENDS)
|
||||
|
||||
val isStreamingEnabled: Boolean
|
||||
get() = preferences.getBoolean(KEY_ENABLE_STREAMING, false)
|
||||
|
||||
val isStreamHomeTimelineEnabled: Boolean
|
||||
get() = preferences.getBoolean("stream_home_timeline", true)
|
||||
|
||||
val isStreamInteractionsEnabled: Boolean
|
||||
get() = preferences.getBoolean("stream_interactions", true)
|
||||
|
||||
val isStreamDirectMessagesEnabled: Boolean
|
||||
get() = preferences.getBoolean("stream_direct_messages", true)
|
||||
|
||||
val isDirectMessagesNotificationEnabled: Boolean
|
||||
get() = preferences.getBoolean(KEY_DIRECT_MESSAGES_NOTIFICATION, DEFAULT_DIRECT_MESSAGES_NOTIFICATION)
|
||||
|
||||
|
@ -8,6 +8,7 @@ class ItemCounts(counts: Int) {
|
||||
private val data: IntArray = IntArray(counts)
|
||||
|
||||
fun getItemCountIndex(itemPosition: Int): Int {
|
||||
if (itemPosition < 0) return -1
|
||||
var sum = 0
|
||||
data.forEachIndexed { i, num ->
|
||||
sum += num
|
||||
|
@ -61,7 +61,7 @@ class ConnectivityStateReceiver : BroadcastReceiver() {
|
||||
appContext.startService(Intent(appContext, UploadLogsService::class.java))
|
||||
}
|
||||
}
|
||||
StreamingService.startOrStopService(context)
|
||||
StreamingService.startOrStopService(appContext)
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
@ -7,6 +7,8 @@ import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.os.Handler
|
||||
import android.os.Looper
|
||||
import android.support.annotation.UiThread
|
||||
import android.support.annotation.WorkerThread
|
||||
import android.support.v4.app.NotificationCompat
|
||||
import android.support.v4.net.ConnectivityManagerCompat
|
||||
import org.apache.commons.lang3.concurrent.BasicThreadFactory
|
||||
@ -18,9 +20,9 @@ import org.mariotaku.library.objectcursor.ObjectCursor
|
||||
import org.mariotaku.microblog.library.MicroBlogException
|
||||
import org.mariotaku.microblog.library.twitter.TwitterUserStream
|
||||
import org.mariotaku.microblog.library.twitter.annotation.StreamWith
|
||||
import org.mariotaku.microblog.library.twitter.model.Activity
|
||||
import org.mariotaku.microblog.library.twitter.model.DirectMessage
|
||||
import org.mariotaku.microblog.library.twitter.model.Status
|
||||
import org.mariotaku.microblog.library.twitter.model.*
|
||||
import org.mariotaku.sqliteqb.library.Columns
|
||||
import org.mariotaku.sqliteqb.library.Expression
|
||||
import org.mariotaku.twidere.R
|
||||
import org.mariotaku.twidere.TwidereConstants.LOGTAG
|
||||
import org.mariotaku.twidere.annotation.AccountType
|
||||
@ -100,28 +102,34 @@ class StreamingService : BaseService() {
|
||||
if (!activityTracker.isHomeActivityLaunched) {
|
||||
return false
|
||||
}
|
||||
// Quit if no connection
|
||||
if (connectivityManager.activeNetworkInfo?.isAvailable != true) {
|
||||
return false
|
||||
}
|
||||
// Quit if connection metered (with preference)
|
||||
val isNetworkMetered = ConnectivityManagerCompat.isActiveNetworkMetered(connectivityManager)
|
||||
if (preferences[streamingNonMeteredNetworkKey] && isNetworkMetered) {
|
||||
return false
|
||||
}
|
||||
// Quit if not charging (with preference)
|
||||
val isCharging = Utils.isCharging(this)
|
||||
if (preferences[streamingPowerSavingKey] && !isCharging) {
|
||||
return false
|
||||
}
|
||||
if (updateStreamingInstances()) {
|
||||
showNotification()
|
||||
return true
|
||||
} else {
|
||||
// Quit if no streaming instance available
|
||||
if (!updateStreamingInstances()) {
|
||||
return false
|
||||
}
|
||||
showNotification()
|
||||
return true
|
||||
}
|
||||
|
||||
private fun updateStreamingInstances(): Boolean {
|
||||
val am = AccountManager.get(this)
|
||||
val supportedAccounts = AccountUtils.getAllAccountDetails(am, true).filter { it.isStreamingSupported }
|
||||
val enabledPrefs = supportedAccounts.map { AccountPreferences(this, it.key) }
|
||||
val supportedPrefs = supportedAccounts.map { AccountPreferences(this, it.key) }
|
||||
val enabledAccounts = supportedAccounts.filter { account ->
|
||||
return@filter enabledPrefs.any {
|
||||
return@filter supportedPrefs.any {
|
||||
account.key == it.accountKey && it.isStreamingEnabled
|
||||
}
|
||||
}
|
||||
@ -138,7 +146,9 @@ class StreamingService : BaseService() {
|
||||
enabledAccounts.forEach { account ->
|
||||
val existing = submittedTasks[account.key]
|
||||
if (existing == null || existing.cancelled) {
|
||||
val runnable = account.newStreamingRunnable() ?: return@forEach
|
||||
val runnable = newStreamingRunnable(account, supportedPrefs.first {
|
||||
it.accountKey == account.key
|
||||
}) ?: return@forEach
|
||||
threadPoolExecutor.submit(runnable)
|
||||
submittedTasks[account.key] = runnable
|
||||
}
|
||||
@ -170,16 +180,20 @@ class StreamingService : BaseService() {
|
||||
|
||||
}
|
||||
|
||||
private fun AccountDetails.newStreamingRunnable(): StreamingRunnable<*>? {
|
||||
when (type) {
|
||||
private fun newStreamingRunnable(account: AccountDetails, preferences: AccountPreferences): StreamingRunnable<*>? {
|
||||
when (account.type) {
|
||||
AccountType.TWITTER -> {
|
||||
return TwitterStreamingRunnable(this@StreamingService, handler, this)
|
||||
return TwitterStreamingRunnable(this, handler, account, preferences)
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
internal abstract class StreamingRunnable<T>(val context: Context, val account: AccountDetails) : Runnable {
|
||||
internal abstract class StreamingRunnable<T>(
|
||||
val context: Context,
|
||||
val account: AccountDetails,
|
||||
val preferences: AccountPreferences
|
||||
) : Runnable {
|
||||
|
||||
var cancelled: Boolean = false
|
||||
private set
|
||||
@ -210,8 +224,12 @@ class StreamingService : BaseService() {
|
||||
abstract fun onCancelled()
|
||||
}
|
||||
|
||||
internal class TwitterStreamingRunnable(context: Context, val handler: Handler, account: AccountDetails) :
|
||||
StreamingRunnable<TwitterUserStream>(context, account) {
|
||||
internal class TwitterStreamingRunnable(
|
||||
context: Context,
|
||||
val handler: Handler,
|
||||
account: AccountDetails,
|
||||
preferences: AccountPreferences
|
||||
) : StreamingRunnable<TwitterUserStream>(context, account, preferences) {
|
||||
|
||||
private val profileImageSize = context.getString(R.string.profile_image_size)
|
||||
private val isOfficial = account.isOfficial(context)
|
||||
@ -241,6 +259,10 @@ class StreamingService : BaseService() {
|
||||
}
|
||||
|
||||
override fun onHomeTimeline(status: Status): Boolean {
|
||||
if (!preferences.isStreamHomeTimelineEnabled) {
|
||||
homeInsertGap = true
|
||||
return false
|
||||
}
|
||||
val parcelableStatus = ParcelableStatusUtils.fromStatus(status, account.key,
|
||||
homeInsertGap, profileImageSize)
|
||||
|
||||
@ -264,10 +286,14 @@ class StreamingService : BaseService() {
|
||||
}
|
||||
|
||||
override fun onActivityAboutMe(activity: Activity): Boolean {
|
||||
if (!preferences.isStreamInteractionsEnabled) {
|
||||
interactionsInsertGap = true
|
||||
return false
|
||||
}
|
||||
if (isOfficial) {
|
||||
// Wait for 30 seconds to avoid rate limit
|
||||
if (canGetInteractions) {
|
||||
getInteractions()
|
||||
handler.post { getInteractions() }
|
||||
canGetInteractions = false
|
||||
handler.postDelayed(interactionsTimeoutRunnable, TimeUnit.SECONDS.toMillis(30))
|
||||
}
|
||||
@ -283,9 +309,13 @@ class StreamingService : BaseService() {
|
||||
return true
|
||||
}
|
||||
|
||||
@WorkerThread
|
||||
override fun onDirectMessage(directMessage: DirectMessage): Boolean {
|
||||
if (!preferences.isStreamDirectMessagesEnabled) {
|
||||
return false
|
||||
}
|
||||
if (canGetMessages) {
|
||||
getMessages()
|
||||
handler.post { getMessages() }
|
||||
canGetMessages = false
|
||||
val timeout = TimeUnit.SECONDS.toMillis(if (isOfficial) 30 else 90)
|
||||
handler.postDelayed(messagesTimeoutRunnable, timeout)
|
||||
@ -298,6 +328,19 @@ class StreamingService : BaseService() {
|
||||
return true
|
||||
}
|
||||
|
||||
override fun onStatusDeleted(event: DeletionEvent): Boolean {
|
||||
val deleteWhere = Expression.and(Expression.likeRaw(Columns.Column(Statuses.ACCOUNT_KEY), "%@||?"),
|
||||
Expression.equalsArgs(Columns.Column(Statuses.STATUS_ID))).sql
|
||||
val deleteWhereArgs = arrayOf(account.key.host, event.id)
|
||||
context.contentResolver.delete(Statuses.CONTENT_URI, deleteWhere, deleteWhereArgs)
|
||||
return true
|
||||
}
|
||||
|
||||
override fun onUnhandledEvent(obj: TwitterStreamObject, json: String) {
|
||||
DebugLog.w(LOGTAG, msg = "Unhandled event ${obj.determine()} for ${account.key}: $json")
|
||||
}
|
||||
|
||||
@UiThread
|
||||
private fun getInteractions() {
|
||||
val task = GetActivitiesAboutMeTask(context)
|
||||
task.params = object : SimpleRefreshTaskParam() {
|
||||
@ -317,6 +360,7 @@ class StreamingService : BaseService() {
|
||||
TaskStarter.execute(task)
|
||||
}
|
||||
|
||||
@UiThread
|
||||
private fun getMessages() {
|
||||
val task = GetMessagesTask(context)
|
||||
task.params = object : GetMessagesTask.RefreshMessagesTaskParam(context) {
|
||||
|
@ -160,7 +160,7 @@ class ContentNotificationManager(
|
||||
Expression.equalsArgs(Activities.ACCOUNT_KEY),
|
||||
Expression.greaterThanArgs(Activities.POSITION_KEY)
|
||||
).sql
|
||||
val whereArgs = arrayOf(accountKey.toString(), "0")
|
||||
val whereArgs = arrayOf(accountKey.toString(), position.toString())
|
||||
@SuppressLint("Recycle")
|
||||
val c = cr.query(Activities.AboutMe.CONTENT_URI, Activities.COLUMNS, where, whereArgs,
|
||||
OrderBy(Activities.TIMESTAMP, false).sql) ?: return
|
||||
|
@ -19,6 +19,7 @@
|
||||
|
||||
package org.mariotaku.twidere.util.streaming
|
||||
|
||||
import android.support.annotation.WorkerThread
|
||||
import org.mariotaku.microblog.library.twitter.callback.SimpleUserStreamCallback
|
||||
import org.mariotaku.microblog.library.twitter.model.*
|
||||
import java.util.*
|
||||
@ -26,7 +27,7 @@ import java.util.*
|
||||
/**
|
||||
* Created by mariotaku on 2017/3/10.
|
||||
*/
|
||||
|
||||
@WorkerThread
|
||||
abstract class TwitterTimelineStreamCallback(val accountId: String) : SimpleUserStreamCallback() {
|
||||
|
||||
private val friends = mutableSetOf<String>()
|
||||
@ -130,9 +131,12 @@ abstract class TwitterTimelineStreamCallback(val accountId: String) : SimpleUser
|
||||
return false
|
||||
}
|
||||
|
||||
@WorkerThread
|
||||
protected abstract fun onHomeTimeline(status: Status): Boolean
|
||||
|
||||
@WorkerThread
|
||||
protected abstract fun onActivityAboutMe(activity: Activity): Boolean
|
||||
|
||||
@WorkerThread
|
||||
override abstract fun onDirectMessage(directMessage: DirectMessage): Boolean
|
||||
}
|
||||
|
@ -1039,6 +1039,10 @@
|
||||
<!-- [noun] Accessibility label for retweet icon -->
|
||||
<string name="status_type_retweet">Retweet</string>
|
||||
|
||||
<string name="stream_type_home">Home</string>
|
||||
<string name="stream_type_interactions">Interactions</string>
|
||||
<string name="stream_type_messages">Messages</string>
|
||||
|
||||
<string name="streaming">Streaming</string>
|
||||
|
||||
<string name="style">Style</string>
|
||||
|
@ -17,9 +17,21 @@
|
||||
~ You should have received a copy of the GNU General Public License
|
||||
~ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
-->
|
||||
|
||||
<!--suppress AndroidElementNotAllowed -->
|
||||
<PreferenceScreen
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:title="@string/title_streaming">
|
||||
|
||||
<SwitchPreferenceCompat
|
||||
android:defaultValue="true"
|
||||
android:key="stream_home_timeline"
|
||||
android:title="@string/stream_type_home"/>
|
||||
<SwitchPreferenceCompat
|
||||
android:defaultValue="true"
|
||||
android:key="stream_interactions"
|
||||
android:title="@string/stream_type_interactions"/>
|
||||
<SwitchPreferenceCompat
|
||||
android:defaultValue="true"
|
||||
android:key="stream_direct_messages"
|
||||
android:title="@string/stream_type_messages"/>
|
||||
</PreferenceScreen>
|
Loading…
x
Reference in New Issue
Block a user