diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/activity/HomeActivity.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/activity/HomeActivity.kt index 7f36ccb4b..42c64cb7e 100644 --- a/twidere/src/main/kotlin/org/mariotaku/twidere/activity/HomeActivity.kt +++ b/twidere/src/main/kotlin/org/mariotaku/twidere/activity/HomeActivity.kt @@ -922,8 +922,8 @@ class HomeActivity : BaseActivity(), OnClickListener, OnPageChangeListener, Supp }.fold(0L, Math::max) val count = DataStoreUtils.getInteractionsCount(context, spec.args, accountKeys, position, Activities.TIMESTAMP) - publishProgress(TabBadge(i, count)) result.put(i, count) + publishProgress(TabBadge(i, count)) } CustomTabType.DIRECT_MESSAGES -> { val accountKeys = Utils.getAccountKeys(context, spec.args) ?: activatedKeys @@ -935,8 +935,8 @@ class HomeActivity : BaseActivity(), OnClickListener, OnPageChangeListener, Supp accountKeys, extraHaving = unreadHaving)?.useCursor { cur -> return@useCursor cur.count } ?: -1 - publishProgress(TabBadge(i, count)) result.put(i, count) + publishProgress(TabBadge(i, count)) } else -> { publishProgress(TabBadge(i, -1)) diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/CursorActivitiesFragment.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/CursorActivitiesFragment.kt index 9ad069ca4..40962d21d 100644 --- a/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/CursorActivitiesFragment.kt +++ b/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/CursorActivitiesFragment.kt @@ -28,8 +28,10 @@ import android.net.Uri import android.os.Bundle import android.os.Handler import android.support.v4.content.Loader +import android.support.v7.widget.RecyclerView import android.widget.Toast import com.squareup.otto.Subscribe +import kotlinx.android.synthetic.main.fragment_content_recyclerview.* import org.mariotaku.ktextension.addOnAccountsUpdatedListenerSafe import org.mariotaku.ktextension.contains import org.mariotaku.ktextension.removeOnAccountsUpdatedListenerSafe @@ -59,24 +61,65 @@ import org.mariotaku.twidere.util.Utils */ abstract class CursorActivitiesFragment : AbsActivitiesFragment() { + override val accountKeys: Array + get() = Utils.getAccountKeys(context, arguments) ?: DataStoreUtils.getActivatedAccountKeys(context) + + abstract val contentUri: Uri + + protected abstract val errorInfoKey: String + + protected abstract val notificationType: Int + + protected abstract val isFilterEnabled: Boolean + private var contentObserver: ContentObserver? = null + private val onScrollListener = object : RecyclerView.OnScrollListener() { + override fun onScrollStateChanged(recyclerView: RecyclerView?, newState: Int) { + if (newState == RecyclerView.SCROLL_STATE_IDLE) { + clearNotifications() + } + } + } + private val accountListener: OnAccountsUpdateListener = OnAccountsUpdateListener { reloadActivities() } - override val accountKeys: Array - get() = Utils.getAccountKeys(context, arguments) ?: DataStoreUtils.getActivatedAccountKeys(context) - - protected abstract val errorInfoKey: String - private val sortOrder: String get() = Activities.DEFAULT_SORT_ORDER - abstract val contentUri: Uri + override fun onStart() { + super.onStart() + if (contentObserver == null) { + contentObserver = object : ContentObserver(Handler()) { + override fun onChange(selfChange: Boolean) { + reloadActivities() + } + } + context.contentResolver.registerContentObserver(Filters.CONTENT_URI, true, contentObserver) + } + AccountManager.get(context).addOnAccountsUpdatedListenerSafe(accountListener, updateImmediately = false) + recyclerView.addOnScrollListener(onScrollListener) + updateRefreshState() + reloadActivities() + } - override fun onContentLoaded(loader: Loader>, data: List?) { - showContentOrError() + override fun onStop() { + recyclerView.removeOnScrollListener(onScrollListener) + if (contentObserver != null) { + context.contentResolver.unregisterContentObserver(contentObserver) + contentObserver = null + } + AccountManager.get(context).removeOnAccountsUpdatedListenerSafe(accountListener) + super.onStop() + } + + override fun setUserVisibleHint(isVisibleToUser: Boolean) { + super.setUserVisibleHint(isVisibleToUser) + if (isVisibleToUser) { + clearNotifications() + } } override fun onCreateActivitiesLoader(context: Context, args: Bundle, fromUser: Boolean): Loader> { @@ -104,34 +147,14 @@ abstract class CursorActivitiesFragment : AbsActivitiesFragment() { sortOrder, fromUser) } + override fun onContentLoaded(loader: Loader>, data: List?) { + showContentOrError() + } + override fun createMessageBusCallback(): Any { return CursorActivitiesBusCallback() } - override fun onStart() { - super.onStart() - if (contentObserver == null) { - contentObserver = object : ContentObserver(Handler()) { - override fun onChange(selfChange: Boolean) { - reloadActivities() - } - } - context.contentResolver.registerContentObserver(Filters.CONTENT_URI, true, contentObserver) - } - AccountManager.get(context).addOnAccountsUpdatedListenerSafe(accountListener, updateImmediately = false) - updateRefreshState() - reloadActivities() - } - - override fun onStop() { - if (contentObserver != null) { - context.contentResolver.unregisterContentObserver(contentObserver) - contentObserver = null - } - AccountManager.get(context).removeOnAccountsUpdatedListenerSafe(accountListener) - super.onStop() - } - override fun hasMoreData(data: List?): Boolean { return data?.size != 0 } @@ -167,7 +190,6 @@ abstract class CursorActivitiesFragment : AbsActivitiesFragment() { }) } - override fun triggerRefresh(): Boolean { super.triggerRefresh() val contentUri = this.contentUri @@ -198,28 +220,12 @@ abstract class CursorActivitiesFragment : AbsActivitiesFragment() { return DataStoreUtils.buildActivityFilterWhereClause(table, null) } - protected abstract val notificationType: Int - - override fun setUserVisibleHint(isVisibleToUser: Boolean) { - super.setUserVisibleHint(isVisibleToUser) - val context = context - if (context != null && isVisibleToUser) { - val accountKeys = accountKeys - for (accountKey in accountKeys) { - twitterWrapper.clearNotificationAsync(notificationType, accountKey) - } - } - } - - protected abstract val isFilterEnabled: Boolean - protected open fun processWhere(where: Expression, whereArgs: Array): ParameterizedExpression { return ParameterizedExpression(where, whereArgs) } protected abstract fun updateRefreshState() - protected fun reloadActivities() { if (activity == null || isDetached) return val args = Bundle() @@ -231,30 +237,6 @@ abstract class CursorActivitiesFragment : AbsActivitiesFragment() { loaderManager.restartLoader(loaderId, args, this) } - private fun showContentOrError() { - val accountKeys = accountKeys - if (adapter.itemCount > 0) { - showContent() - } else if (accountKeys.isNotEmpty()) { - val errorInfo = ErrorInfoStore.getErrorInfo(context, - errorInfoStore[errorInfoKey, accountKeys[0]]) - if (errorInfo != null) { - showEmpty(errorInfo.icon, errorInfo.message) - } else { - showEmpty(R.drawable.ic_info_refresh, getString(R.string.swipe_down_to_refresh)) - } - } else { - showError(R.drawable.ic_info_accounts, getString(R.string.message_toast_no_account_selected)) - } - } - - - private fun updateFavoritedStatus(status: ParcelableStatus) { - activity ?: return - replaceStatusStates(status) - } - - fun replaceStatusStates(result: ParcelableStatus?) { if (result == null) return val lm = layoutManager @@ -283,6 +265,36 @@ abstract class CursorActivitiesFragment : AbsActivitiesFragment() { adapter.notifyItemRangeChanged(rangeStart, rangeEnd) } + private fun showContentOrError() { + val accountKeys = accountKeys + if (adapter.itemCount > 0) { + showContent() + } else if (accountKeys.isNotEmpty()) { + val errorInfo = ErrorInfoStore.getErrorInfo(context, + errorInfoStore[errorInfoKey, accountKeys[0]]) + if (errorInfo != null) { + showEmpty(errorInfo.icon, errorInfo.message) + } else { + showEmpty(R.drawable.ic_info_refresh, getString(R.string.swipe_down_to_refresh)) + } + } else { + showError(R.drawable.ic_info_accounts, getString(R.string.message_toast_no_account_selected)) + } + } + + private fun updateFavoritedStatus(status: ParcelableStatus) { + activity ?: return + replaceStatusStates(status) + } + + private fun clearNotifications() { + if (context != null && userVisibleHint) { + for (accountKey in accountKeys) { + twitterWrapper.clearNotificationAsync(notificationType, accountKey) + } + } + } + protected inner class CursorActivitiesBusCallback { @Subscribe diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/CursorStatusesFragment.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/CursorStatusesFragment.kt index 76f941aca..a1ba047ca 100644 --- a/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/CursorStatusesFragment.kt +++ b/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/CursorStatusesFragment.kt @@ -27,6 +27,7 @@ import android.net.Uri import android.os.Bundle import android.os.Handler import android.support.v4.content.Loader +import android.support.v7.widget.RecyclerView import android.widget.Toast import com.bumptech.glide.Glide import com.squareup.otto.Subscribe @@ -62,23 +63,63 @@ import org.mariotaku.twidere.util.buildStatusFilterWhereClause */ abstract class CursorStatusesFragment : AbsStatusesFragment() { - private var contentObserver: ContentObserver? = null - private val accountListener: OnAccountsUpdateListener = OnAccountsUpdateListener { - reloadStatuses() - } - - abstract val errorInfoKey: String - abstract val isFilterEnabled: Boolean - abstract val notificationType: Int - abstract val contentUri: Uri override var refreshing: Boolean get() = swipeLayout.isRefreshing set(value) { super.refreshing = value } + override val useSortIdAsReadPosition: Boolean get() = false + override val accountKeys: Array + get() = Utils.getAccountKeys(context, arguments) ?: DataStoreUtils.getActivatedAccountKeys(context) + + abstract val errorInfoKey: String + abstract val isFilterEnabled: Boolean + abstract val notificationType: Int + abstract val contentUri: Uri + + private var contentObserver: ContentObserver? = null + private val accountListener: OnAccountsUpdateListener = OnAccountsUpdateListener { + reloadStatuses() + } + + private val onScrollListener = object : RecyclerView.OnScrollListener() { + override fun onScrollStateChanged(recyclerView: RecyclerView?, newState: Int) { + if (newState == RecyclerView.SCROLL_STATE_IDLE) { + clearNotifications() + } + } + } + + override fun onStart() { + super.onStart() + if (contentObserver == null) { + contentObserver = object : ContentObserver(Handler()) { + override fun onChange(selfChange: Boolean) { + reloadStatuses() + } + } + context.contentResolver.registerContentObserver(Filters.CONTENT_URI, true, contentObserver) + } + AccountManager.get(context).addOnAccountsUpdatedListenerSafe(accountListener, updateImmediately = false) + recyclerView.addOnScrollListener(onScrollListener) + updateRefreshState() + reloadStatuses() + } + + override fun onStop() { + recyclerView.removeOnScrollListener(onScrollListener) + if (contentObserver != null) { + context.contentResolver.unregisterContentObserver(contentObserver) + contentObserver = null + } + AccountManager.get(context).removeOnAccountsUpdatedListenerSafe(accountListener) + super.onStop() + } + + override fun onStatusesLoaded(loader: Loader?>, data: List?) { showContentOrError() } @@ -110,61 +151,6 @@ abstract class CursorStatusesFragment : AbsStatusesFragment() { return CursorStatusesBusCallback() } - private fun showContentOrError() { - val accountKeys = this.accountKeys - if (adapter.itemCount > 0) { - showContent() - } else if (accountKeys.isNotEmpty()) { - val errorInfo = ErrorInfoStore.getErrorInfo(context, - errorInfoStore[errorInfoKey, accountKeys[0]]) - if (errorInfo != null) { - showEmpty(errorInfo.icon, errorInfo.message) - } else { - showEmpty(R.drawable.ic_info_refresh, getString(R.string.swipe_down_to_refresh)) - } - } else { - showError(R.drawable.ic_info_accounts, getString(R.string.message_toast_no_account_selected)) - } - } - - override val accountKeys: Array - get() = Utils.getAccountKeys(context, arguments) ?: DataStoreUtils.getActivatedAccountKeys(context) - - override fun onStart() { - super.onStart() - if (contentObserver == null) { - contentObserver = object : ContentObserver(Handler()) { - override fun onChange(selfChange: Boolean) { - reloadStatuses() - } - } - context.contentResolver.registerContentObserver(Filters.CONTENT_URI, true, contentObserver) - } - AccountManager.get(context).addOnAccountsUpdatedListenerSafe(accountListener, updateImmediately = false) - updateRefreshState() - reloadStatuses() - } - - override fun onStop() { - if (contentObserver != null) { - context.contentResolver.unregisterContentObserver(contentObserver) - contentObserver = null - } - AccountManager.get(context).removeOnAccountsUpdatedListenerSafe(accountListener) - super.onStop() - } - - protected fun reloadStatuses() { - if (context == null || isDetached) return - val args = Bundle() - val fragmentArgs = arguments - if (fragmentArgs != null) { - args.putAll(fragmentArgs) - args.putBoolean(EXTRA_FROM_USER, true) - } - loaderManager.restartLoader(loaderId, args, this) - } - override fun hasMoreData(data: List?): Boolean { return data?.size != 0 } @@ -221,27 +207,60 @@ abstract class CursorStatusesFragment : AbsStatusesFragment() { return true } + override fun setUserVisibleHint(isVisibleToUser: Boolean) { + super.setUserVisibleHint(isVisibleToUser) + if (isVisibleToUser) { + clearNotifications() + } + } + protected fun getFiltersWhere(table: String): Expression? { if (!isFilterEnabled) return null return buildStatusFilterWhereClause(preferences, table, null) } - override fun setUserVisibleHint(isVisibleToUser: Boolean) { - super.setUserVisibleHint(isVisibleToUser) - val context = context - if (context != null && isVisibleToUser) { - for (accountKey in accountKeys) { - twitterWrapper.clearNotificationAsync(notificationType, accountKey) - } - } - } - protected open fun processWhere(where: Expression, whereArgs: Array): ParameterizedExpression { return ParameterizedExpression(where, whereArgs) } protected abstract fun updateRefreshState() + protected fun reloadStatuses() { + if (context == null || isDetached) return + val args = Bundle() + val fragmentArgs = arguments + if (fragmentArgs != null) { + args.putAll(fragmentArgs) + args.putBoolean(EXTRA_FROM_USER, true) + } + loaderManager.restartLoader(loaderId, args, this) + } + + private fun showContentOrError() { + val accountKeys = this.accountKeys + if (adapter.itemCount > 0) { + showContent() + } else if (accountKeys.isNotEmpty()) { + val errorInfo = ErrorInfoStore.getErrorInfo(context, + errorInfoStore[errorInfoKey, accountKeys[0]]) + if (errorInfo != null) { + showEmpty(errorInfo.icon, errorInfo.message) + } else { + showEmpty(R.drawable.ic_info_refresh, getString(R.string.swipe_down_to_refresh)) + } + } else { + showError(R.drawable.ic_info_accounts, getString(R.string.message_toast_no_account_selected)) + } + } + + private fun clearNotifications() { + if (context != null && userVisibleHint) { + for (accountKey in accountKeys) { + twitterWrapper.clearNotificationAsync(notificationType, accountKey) + } + } + } + protected inner class CursorStatusesBusCallback { @Subscribe 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 9d8171ac2..45865bef6 100644 --- a/twidere/src/main/kotlin/org/mariotaku/twidere/util/ContentNotificationManager.kt +++ b/twidere/src/main/kotlin/org/mariotaku/twidere/util/ContentNotificationManager.kt @@ -81,7 +81,9 @@ class ContentNotificationManager( val accountKey = pref.accountKey val resources = context.resources val selection = Expression.and(Expression.equalsArgs(Statuses.ACCOUNT_KEY), - Expression.greaterThan(Statuses.POSITION_KEY, minPositionKey)) + Expression.greaterThan(Statuses.POSITION_KEY, minPositionKey), + Expression.notEquals(Statuses.IS_GAP, 1) + ) val selectionArgs = arrayOf(accountKey.toString()) val filteredSelection = buildStatusFilterWhereClause(preferences, Statuses.TABLE_NAME, selection) @@ -164,7 +166,8 @@ class ContentNotificationManager( val accountKey = pref.accountKey val where = Expression.and( Expression.equalsArgs(Activities.ACCOUNT_KEY), - Expression.greaterThan(Activities.POSITION_KEY, position) + Expression.greaterThan(Activities.POSITION_KEY, position), + Expression.notEquals(Activities.IS_GAP, 1) ).sql val whereArgs = arrayOf(accountKey.toString()) @SuppressLint("Recycle") diff --git a/twidere/src/main/res/values/arrays_languages.xml b/twidere/src/main/res/values/arrays_languages.xml index 2613c5aa1..97998c05a 100644 --- a/twidere/src/main/res/values/arrays_languages.xml +++ b/twidere/src/main/res/values/arrays_languages.xml @@ -31,6 +31,7 @@ de el en_GB + en_US es fa fi @@ -77,7 +78,8 @@ Danish German Greek - English (UK) + English (GB) + English (United States) Spanish Persian Finnish