1
0
mirror of https://github.com/TwidereProject/Twidere-Android synced 2025-02-17 04:00:48 +01:00

improved load more position remembering

This commit is contained in:
Mariotaku Lee 2016-12-24 20:47:35 +08:00
parent 0de5011e80
commit d0c05a2600
30 changed files with 414 additions and 307 deletions

View File

@ -21,6 +21,7 @@ package org.mariotaku.twidere.model;
import android.os.Parcel;
import android.os.Parcelable;
import android.support.annotation.IntDef;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.text.TextUtils;
@ -353,6 +354,11 @@ public class ParcelableStatus implements Parcelable, Comparable<ParcelableStatus
@ParcelableThisPlease
public boolean is_pinned_status;
@FilterFlags
@CursorField(Statuses.FILTER_FLAGS)
@ParcelableThisPlease
public long filter_flags;
public ParcelableStatus() {
}
@ -557,4 +563,35 @@ public class ParcelableStatus implements Parcelable, Comparable<ParcelableStatus
ParcelableStatus$ExtrasParcelablePlease.writeToParcel(this, dest, flags);
}
}
/**
* Flags for filtering some kind of tweet.
* We use bitwise operations against string comparisons because it's much faster.
* <p>
* DO NOT CHANGE ONCE DEFINED!
*/
@IntDef(value = {
FilterFlags.QUOTE_NOT_AVAILABLE,
FilterFlags.BLOCKING_USER,
FilterFlags.BLOCKED_BY_USER
}, flag = true)
public @interface FilterFlags {
/**
* Original tweet of a quote tweet is unavailable.
* Happens when:
* <p>
* <li/>You were blocked by this user
* <li/>You blocked/muted this user
* <li/>Original tweet was marked sensitive and your account settings blocked them
*/
long QUOTE_NOT_AVAILABLE = 0x1;
/**
* Original author of a quote/retweet was blocked by you
*/
long BLOCKING_USER = 0x2;
/**
* You were blocked by original author of a quote/retweet
*/
long BLOCKED_BY_USER = 0x4;
}
}

View File

@ -852,6 +852,7 @@ public interface TwidereDataStore {
String RETWEET_USER_NICKNAME = "retweet_user_nickname";
String IN_REPLY_TO_USER_NICKNAME = "in_reply_to_user_nickname";
String FILTER_FLAGS = "filter_flags";
String DEFAULT_SORT_ORDER = STATUS_TIMESTAMP + " DESC, " + SORT_ID + " DESC, " + STATUS_ID
+ " DESC";

View File

@ -173,7 +173,7 @@ dependencies {
compile "com.github.mariotaku.CommonsLibrary:text:$mariotaku_commons_library_version"
compile "com.github.mariotaku.CommonsLibrary:text-kotlin:$mariotaku_commons_library_version"
compile 'com.github.mariotaku:KPreferences:0.9.5'
compile 'com.github.mariotaku:Chameleon:0.9.1'
compile 'com.github.mariotaku:Chameleon:0.9.2'
compile "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
compile 'nl.komponents.kovenant:kovenant:3.3.0'
compile 'nl.komponents.kovenant:kovenant-android:3.3.0'

View File

@ -108,33 +108,11 @@ class GoogleMapFragment : SupportMapFragment(), Constants, IMapFragment, IBaseFr
}
}
override val extraConfiguration: Bundle? = null
override val tabPosition: Int = -1
override val tabId: Long = -1L
override fun requestFitSystemWindows() {
val activity = activity
val parentFragment = parentFragment
val callback: IBaseFragment.SystemWindowsInsetsCallback
if (parentFragment is IBaseFragment.SystemWindowsInsetsCallback) {
callback = parentFragment
} else if (activity is IBaseFragment.SystemWindowsInsetsCallback) {
callback = activity
} else {
return
}
val insets = Rect()
if (callback.getSystemWindowsInsets(insets)) {
fitSystemWindows(insets)
}
}
override fun executeAfterFragmentResumed(action: (IBaseFragment) -> Unit) {
actionHelper.executeAfterFragmentResumed(action)
}
private fun fitSystemWindows(insets: Rect) {
override fun fitSystemWindows(insets: Rect) {
val view = view
view?.setPadding(insets.left, insets.top, insets.right, insets.bottom)
}

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 = 154;
int DATABASES_VERSION = 155;
int MENU_GROUP_STATUS_EXTENSION = 10;
int MENU_GROUP_COMPOSE_EXTENSION = 11;

View File

@ -1,83 +0,0 @@
package org.mariotaku.twidere.util;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.widget.AbsListView;
import org.mariotaku.twidere.util.support.ViewSupport;
/**
* Created by mariotaku on 16/3/1.
*/
public class ListViewScrollHandler extends ContentScrollHandler implements AbsListView.OnScrollListener,
ListScrollDistanceCalculator.ScrollDistanceListener {
private final ListScrollDistanceCalculator mCalculator;
@Nullable
private AbsListView.OnScrollListener mOnScrollListener;
private int mDy;
private int mOldState = AbsListView.OnScrollListener.SCROLL_STATE_IDLE;
public ListViewScrollHandler(@NonNull ContentListSupport contentListSupport, @Nullable ViewCallback viewCallback) {
super(contentListSupport, viewCallback);
mCalculator = new ListScrollDistanceCalculator();
}
@Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
mCalculator.onScrollStateChanged(view, scrollState);
mCalculator.setScrollDistanceListener(this);
handleScrollStateChanged(scrollState, SCROLL_STATE_IDLE);
if (mOnScrollListener != null) {
mOnScrollListener.onScrollStateChanged(view, scrollState);
}
}
@Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
mCalculator.onScroll(view, firstVisibleItem, visibleItemCount, totalItemCount);
final int scrollState = getScrollState();
handleScroll(mDy, scrollState, mOldState, SCROLL_STATE_IDLE);
if (mOnScrollListener != null) {
mOnScrollListener.onScroll(view, firstVisibleItem, visibleItemCount, totalItemCount);
}
}
@Nullable
public AbsListView.OnScrollListener getOnScrollListener() {
return mOnScrollListener;
}
public void setOnScrollListener(@Nullable AbsListView.OnScrollListener onScrollListener) {
mOnScrollListener = onScrollListener;
}
public int getTotalScrollDistance() {
return mCalculator.getTotalScrollDistance();
}
@Override
public void onScrollDistanceChanged(int delta, int total) {
mDy = -delta;
final int scrollState = getScrollState();
handleScroll(mDy, scrollState, mOldState, SCROLL_STATE_IDLE);
mOldState = scrollState;
}
public static class ListViewCallback implements ViewCallback {
private final AbsListView listView;
public ListViewCallback(AbsListView listView) {
this.listView = listView;
}
@Override
public boolean getComputingLayout() {
return ViewSupport.isInLayout(listView);
}
@Override
public void post(@NonNull Runnable runnable) {
listView.post(runnable);
}
}
}

View File

@ -0,0 +1,13 @@
package org.mariotaku.ktextension
/**
* Created by mariotaku on 2016/12/24.
*/
fun Collection<*>?.isNotNullOrEmpty(): Boolean {
return this != null && this.isNotEmpty()
}
fun Collection<*>?.isNullOrEmpty(): Boolean {
return this == null || this.isEmpty()
}

View File

@ -11,4 +11,8 @@ inline fun <T> configure(receiver: T, block: T.() -> Unit): T {
inline fun <F, T> F.convert(block: (F) -> T): T {
return block(this)
}
fun rangeOfSize(start: Int, size: Int): IntRange {
return IntRange(start, start + size)
}

View File

@ -714,11 +714,10 @@ class SignInActivity : BaseActivity(), OnClickListener, TextWatcher {
builder.setPositiveButton(android.R.string.ok) { dialog, which ->
val editConsumerKey = (dialog as Dialog).findViewById(R.id.editConsumerKey) as EditText
val editConsumerSecret = dialog.findViewById(R.id.editConsumerSecret) as EditText
val prefs = SharedPreferencesWrapper.getInstance(activity, SHARED_PREFERENCES_NAME, Context.MODE_PRIVATE)
val editor = prefs.edit()
editor.putString(KEY_CONSUMER_KEY, ParseUtils.parseString(editConsumerKey.text))
editor.putString(KEY_CONSUMER_SECRET, ParseUtils.parseString(editConsumerSecret.text))
editor.apply()
val apiConfig = kPreferences[defaultAPIConfigKey]
apiConfig.consumerKey = editConsumerKey.text.toString()
apiConfig.consumerSecret = editConsumerSecret.text.toString()
kPreferences[defaultAPIConfigKey] = apiConfig
}
val dialog = builder.create()
dialog.setOnShowListener(DialogInterface.OnShowListener { dialog ->
@ -727,9 +726,9 @@ class SignInActivity : BaseActivity(), OnClickListener, TextWatcher {
val editConsumerSecret = dialog.findViewById(R.id.editConsumerSecret) as MaterialEditText
editConsumerKey.addValidator(ConsumerKeySecretValidator(getString(R.string.invalid_consumer_key)))
editConsumerSecret.addValidator(ConsumerKeySecretValidator(getString(R.string.invalid_consumer_secret)))
val prefs = SharedPreferencesWrapper.getInstance(activity, SHARED_PREFERENCES_NAME, Context.MODE_PRIVATE)
editConsumerKey.setText(prefs.getString(KEY_CONSUMER_KEY, null))
editConsumerSecret.setText(prefs.getString(KEY_CONSUMER_SECRET, null))
val apiConfig = kPreferences[defaultAPIConfigKey]
editConsumerKey.setText(apiConfig.consumerKey)
editConsumerSecret.setText(apiConfig.consumerSecret)
})
return dialog
}

View File

@ -20,11 +20,13 @@
package org.mariotaku.twidere.adapter
import android.content.Context
import android.database.Cursor
import android.support.v4.widget.Space
import android.support.v7.widget.RecyclerView
import android.view.LayoutInflater
import android.view.ViewGroup
import org.mariotaku.ktextension.findPositionByItemId
import org.mariotaku.ktextension.rangeOfSize
import org.mariotaku.ktextension.safeMoveToPosition
import org.mariotaku.library.objectcursor.ObjectCursor
import org.mariotaku.twidere.R
@ -176,58 +178,49 @@ abstract class ParcelableStatusesAdapter(
get() = data?.size ?: 0
override fun getItemId(position: Int): Long {
val dataPosition = position - getItemStartPosition(2)
if (dataPosition < 0 || dataPosition >= statusCount) return position.toLong()
if (data is ObjectCursor) {
val cursor = (data as ObjectCursor).cursor
if (!cursor.safeMoveToPosition(dataPosition)) return -1
val indices = (data as ObjectCursor).indices as ParcelableStatusCursorIndices
return getFieldValue(position, { cursor, indices ->
val accountKey = UserKey.valueOf(cursor.getString(indices.account_key))
val id = cursor.getString(indices.id)
return ParcelableStatus.calculateHashCode(accountKey, id).toLong()
}
return data!![dataPosition].hashCode().toLong()
return@getFieldValue ParcelableStatus.calculateHashCode(accountKey, id).toLong()
}, { status ->
return@getFieldValue status.hashCode().toLong()
}, -1L)
}
override fun getStatusId(position: Int): String? {
val dataPosition = position - getItemStartPosition(2)
if (dataPosition < 0 || dataPosition >= rawStatusCount) return null
if (data is ObjectCursor) {
val cursor = (data as ObjectCursor).cursor
if (!cursor.safeMoveToPosition(dataPosition)) return null
val indices = (data as ObjectCursor).indices as ParcelableStatusCursorIndices
return cursor.getString(indices.id)
}
return data!![dataPosition].id
return getFieldValue<String?>(position, { cursor, indices ->
return@getFieldValue cursor.getString(indices.id)
}, { status ->
return@getFieldValue status.id
}, null)
}
fun getStatusSortId(position: Int): Long {
return getFieldValue(position, { cursor, indices ->
return@getFieldValue cursor.getLong(indices.sort_id)
}, { status ->
return@getFieldValue status.sort_id
}, -1L)
}
override fun getStatusTimestamp(position: Int): Long {
val dataPosition = position - getItemStartPosition(2)
if (dataPosition < 0 || dataPosition >= rawStatusCount) return -1
if (data is ObjectCursor) {
val cursor = (data as ObjectCursor).cursor
if (!cursor.safeMoveToPosition(dataPosition)) return -1
val indices = (data as ObjectCursor).indices as ParcelableStatusCursorIndices
return cursor.getLong(indices.timestamp)
}
return data!![dataPosition].timestamp
return getFieldValue(position, { cursor, indices ->
return@getFieldValue cursor.getLong(indices.timestamp)
}, { status ->
return@getFieldValue status.timestamp
}, -1L)
}
override fun getStatusPositionKey(position: Int): Long {
val dataPosition = position - getItemStartPosition(2)
if (dataPosition < 0 || dataPosition >= rawStatusCount) return -1
if (data is ObjectCursor) {
val cursor = (data as ObjectCursor).cursor
if (!cursor.safeMoveToPosition(dataPosition)) return -1
val indices = (data as ObjectCursor).indices as ParcelableStatusCursorIndices
return getFieldValue(position, { cursor, indices ->
val positionKey = cursor.getLong(indices.position_key)
if (positionKey > 0) return positionKey
return cursor.getLong(indices.timestamp)
}
val status = data!![dataPosition]
val positionKey = status.position_key
if (positionKey > 0) return positionKey
return status.timestamp
if (positionKey > 0) return@getFieldValue positionKey
return@getFieldValue cursor.getLong(indices.timestamp)
}, { status ->
val positionKey = status.position_key
if (positionKey > 0) return@getFieldValue positionKey
return@getFieldValue status.timestamp
}, -1L)
}
override fun getAccountKey(position: Int): UserKey? {
@ -295,7 +288,6 @@ abstract class ParcelableStatusesAdapter(
throw IllegalStateException("Unknown view type " + viewType)
}
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
when (getItemCountIndex(position)) {
1 -> {
@ -318,6 +310,7 @@ abstract class ParcelableStatusesAdapter(
}
protected abstract fun onCreateStatusViewHolder(parent: ViewGroup): IStatusViewHolder
override fun addGapLoadingId(id: ObjectId) {
@ -360,7 +353,6 @@ abstract class ParcelableStatusesAdapter(
throw AssertionError()
}
override fun getItemCount(): Int {
val position = loadMoreIndicatorPosition
itemCounts[0] = if (position and ILoadMoreSupportAdapter.START != 0L) 1 else 0
@ -380,9 +372,28 @@ abstract class ParcelableStatusesAdapter(
return null
}
val statusStartIndex: Int
get() = getItemStartPosition(2)
private inline fun <T> getFieldValue(
position: Int,
readCursorValueAction: (cursor: Cursor, indices: ParcelableStatusCursorIndices) -> T,
readStatusValueAction: (status: ParcelableStatus) -> T,
defValue: T
): T {
val dataPosition = position - getItemStartPosition(2)
if (dataPosition < 0 || dataPosition >= rawStatusCount) return defValue
if (data is ObjectCursor) {
val cursor = (data as ObjectCursor).cursor
if (!cursor.safeMoveToPosition(dataPosition)) return defValue
val indices = (data as ObjectCursor).indices as ParcelableStatusCursorIndices
return readCursorValueAction(cursor, indices)
}
val status = data!![dataPosition]
return readStatusValueAction(status)
}
private fun isFiltered(position: Int): Boolean {
if (data is ObjectCursor) return false
return getStatus(position)!!.is_filtered
@ -393,5 +404,17 @@ abstract class ParcelableStatusesAdapter(
const val ITEM_VIEW_TYPE_EMPTY = 3
}
fun findPositionByPositionKey(positionKey: Long): Int {
// Assume statuses are descend sorted by id, so break at first status with id
// lesser equals than read position
return rangeOfSize(statusStartIndex, statusCount - 1).indexOfFirst { positionKey > 0 && getStatusPositionKey(it) <= positionKey }
}
fun findPositionBySortId(sortId: Long): Int {
// Assume statuses are descend sorted by id, so break at first status with id
// lesser equals than read position
return rangeOfSize(statusStartIndex, statusCount - 1).indexOfFirst { sortId > 0 && getStatusSortId(it) <= sortId }
}
}

View File

@ -8,6 +8,7 @@ import org.mariotaku.ktextension.toLong
import org.mariotaku.twidere.BuildConfig
import org.mariotaku.twidere.Constants.KEY_NO_CLOSE_AFTER_TWEET_SENT
import org.mariotaku.twidere.TwidereConstants.*
import org.mariotaku.twidere.constant.SharedPreferenceConstants.KEY_REMEMBER_POSITION
import org.mariotaku.twidere.extension.getNonEmptyString
import org.mariotaku.twidere.model.CustomAPIConfig
import org.mariotaku.twidere.model.account.cred.Credentials
@ -31,6 +32,7 @@ val linkHighlightOptionKey = KStringKey(KEY_LINK_HIGHLIGHT_OPTION, VALUE_LINK_HI
val statusShortenerKey = KNullableStringKey(KEY_STATUS_SHORTENER, null)
val mediaUploaderKey = KNullableStringKey(KEY_MEDIA_UPLOADER, null)
val newDocumentApiKey = KBooleanKey(KEY_NEW_DOCUMENT_API, Build.VERSION.SDK_INT == Build.VERSION_CODES.M)
val rememberPositionKey = KBooleanKey(KEY_REMEMBER_POSITION, true)
val attachLocationKey = KBooleanKey(KEY_ATTACH_LOCATION, false)
val attachPreciseLocationKey = KBooleanKey(KEY_ATTACH_PRECISE_LOCATION, false)
val noCloseAfterTweetSentKey = KBooleanKey(KEY_NO_CLOSE_AFTER_TWEET_SENT, false)

View File

@ -38,7 +38,7 @@ import edu.tsinghua.hotmobi.model.MediaEvent
import kotlinx.android.synthetic.main.fragment_content_recyclerview.*
import org.mariotaku.kpreferences.get
import org.mariotaku.twidere.BuildConfig
import org.mariotaku.twidere.Constants.*
import org.mariotaku.twidere.Constants.KEY_NEW_DOCUMENT_API
import org.mariotaku.twidere.R
import org.mariotaku.twidere.TwidereConstants
import org.mariotaku.twidere.adapter.ParcelableActivitiesAdapter
@ -52,6 +52,7 @@ import org.mariotaku.twidere.annotation.ReadPositionTag
import org.mariotaku.twidere.constant.IntentConstants
import org.mariotaku.twidere.constant.KeyboardShortcutConstants.*
import org.mariotaku.twidere.constant.readFromBottomKey
import org.mariotaku.twidere.constant.rememberPositionKey
import org.mariotaku.twidere.extension.getAccountType
import org.mariotaku.twidere.fragment.AbsStatusesFragment.DefaultOnLikedListener
import org.mariotaku.twidere.loader.iface.IExtendedLoader
@ -71,7 +72,10 @@ import org.mariotaku.twidere.view.holder.StatusViewHolder
import org.mariotaku.twidere.view.holder.iface.IStatusViewHolder
import java.util.*
abstract class AbsActivitiesFragment protected constructor() : AbsContentListRecyclerViewFragment<ParcelableActivitiesAdapter>(), LoaderCallbacks<List<ParcelableActivity>>, ParcelableActivitiesAdapter.ActivityAdapterListener, KeyboardShortcutCallback {
abstract class AbsActivitiesFragment protected constructor() :
AbsContentListRecyclerViewFragment<ParcelableActivitiesAdapter>(),
LoaderCallbacks<List<ParcelableActivity>>, ParcelableActivitiesAdapter.ActivityAdapterListener,
KeyboardShortcutCallback {
private lateinit var activitiesBusCallback: Any
private lateinit var navigationHelper: RecyclerViewNavigationHelper
@ -193,8 +197,8 @@ abstract class AbsActivitiesFragment protected constructor() : AbsContentListRec
}
override fun onLoadFinished(loader: Loader<List<ParcelableActivity>>, data: List<ParcelableActivity>) {
val rememberPosition = preferences.getBoolean(KEY_REMEMBER_POSITION, false)
val readFromBottom = preferences.getBoolean(KEY_READ_FROM_BOTTOM, false)
val rememberPosition = preferences[rememberPositionKey]
val readFromBottom = preferences[readFromBottomKey]
var lastReadId: Long
val lastVisiblePos: Int
val lastVisibleTop: Int

View File

@ -41,7 +41,9 @@ import org.mariotaku.twidere.util.TwidereColorUtils
/**
* Created by mariotaku on 15/4/16.
*/
abstract class AbsContentListViewFragment<A : ListAdapter> : BaseSupportFragment(), OnRefreshListener, RefreshScrollTopInterface, ControlBarOffsetListener, ContentListSupport, AbsListView.OnScrollListener {
abstract class AbsContentListViewFragment<A : ListAdapter> : BaseSupportFragment(),
OnRefreshListener, RefreshScrollTopInterface, ControlBarOffsetListener, ContentListSupport,
AbsListView.OnScrollListener {
private lateinit var scrollHandler: ListViewScrollHandler
override lateinit var adapter: A
@ -105,8 +107,8 @@ abstract class AbsContentListViewFragment<A : ListAdapter> : BaseSupportFragment
super.onDetach()
}
override fun onCreateView(inflater: LayoutInflater?, container: ViewGroup?, savedInstanceState: Bundle?): View? {
return inflater!!.inflate(R.layout.fragment_content_listview, container, false)
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
return inflater.inflate(R.layout.fragment_content_listview, container, false)
}
override fun onActivityCreated(savedInstanceState: Bundle?) {

View File

@ -37,6 +37,8 @@ import edu.tsinghua.hotmobi.HotMobiLogger
import edu.tsinghua.hotmobi.model.MediaEvent
import kotlinx.android.synthetic.main.fragment_content_recyclerview.*
import org.mariotaku.kpreferences.get
import org.mariotaku.ktextension.isNullOrEmpty
import org.mariotaku.ktextension.rangeOfSize
import org.mariotaku.twidere.BuildConfig
import org.mariotaku.twidere.R
import org.mariotaku.twidere.TwidereConstants
@ -49,6 +51,7 @@ import org.mariotaku.twidere.constant.IntentConstants.*
import org.mariotaku.twidere.constant.KeyboardShortcutConstants.*
import org.mariotaku.twidere.constant.SharedPreferenceConstants
import org.mariotaku.twidere.constant.readFromBottomKey
import org.mariotaku.twidere.constant.rememberPositionKey
import org.mariotaku.twidere.extension.getAccountType
import org.mariotaku.twidere.graphic.like.LikeAnimationDrawable
import org.mariotaku.twidere.loader.iface.IExtendedLoader
@ -92,23 +95,25 @@ abstract class AbsStatusesFragment protected constructor() :
protected abstract val accountKeys: Array<UserKey>
protected var adapterData: List<ParcelableStatus>?
get() {
return adapter.getData()
}
get() = adapter.getData()
set(data) {
adapter.setData(data)
}
@ReadPositionTag
protected open val readPositionTag: String?
@ReadPositionTag
get() = null
protected open val readPositionTagWithArguments: String?
get() = readPositionTag
private val currentReadPositionTag: String?
get() {
if (readPositionTag == null || tabId < 0) return null
return "${readPositionTag}_${tabId}_current"
}
protected open val useSortIdAsReadPosition: Boolean = true
/**
* Used for 'restore position' feature
*/
protected open val currentReadPositionTag: String?
get() = if (readPositionTag == null || tabId < 0) null else "${readPositionTag}_${tabId}_current"
override val extraContentPadding: Rect
get() {
@ -121,11 +126,10 @@ abstract class AbsStatusesFragment protected constructor() :
// Fragment life cycles
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
statusesBusCallback = createMessageBusCallback()
scrollListener?.reversed = preferences[readFromBottomKey]
scrollListener.reversed = preferences[readFromBottomKey]
adapter.statusClickListener = this
registerForContextMenu(recyclerView)
navigationHelper = RecyclerViewNavigationHelper(recyclerView, layoutManager, adapter, this)
@ -244,45 +248,58 @@ abstract class AbsStatusesFragment protected constructor() :
super.setUserVisibleHint(isVisibleToUser)
}
/**
* Statuses loaded, update adapter data & restore load position
*
* Steps:
* 1. Save current read position if not first load (adapter data is not empty)
* 1.1 If readFromBottom is true, save position on screen bottom
* 2. Change adapter data
* 3. Restore adapter data
* 3.1 If lastVisible was last item, keep lastVisibleItem position (load more)
* 3.2 Else, if readFromBottom is true:
* 3.1.1 If position was first, keep lastVisibleItem position (pull refresh)
* 3.1.2 Else, keep lastVisibleItem position
* 3.2 If readFromBottom is false:
* 3.2.1 If position was first, set new position to 0 (pull refresh)
* 3.2.2 Else, keep firstVisibleItem position (gap clicked)
*/
override fun onLoadFinished(loader: Loader<List<ParcelableStatus>?>, data: List<ParcelableStatus>?) {
val rememberPosition = preferences.getBoolean(SharedPreferenceConstants.KEY_REMEMBER_POSITION, false)
val readFromBottom = preferences.getBoolean(SharedPreferenceConstants.KEY_READ_FROM_BOTTOM, false)
var lastReadPositionKey: Long
val lastVisiblePos: Int
val lastVisibleTop: Int
val tag = currentReadPositionTag
val layoutManager = layoutManager
if (readFromBottom) {
lastVisiblePos = layoutManager.findLastVisibleItemPosition()
} else {
lastVisiblePos = layoutManager.findFirstVisibleItemPosition()
}
if (lastVisiblePos != RecyclerView.NO_POSITION && lastVisiblePos < adapter.itemCount) {
val statusStartIndex = adapter.statusStartIndex
val statusEndIndex = statusStartIndex + adapter.statusCount
val lastItemIndex = Math.min(statusEndIndex, lastVisiblePos)
lastReadPositionKey = adapter.getStatusPositionKey(lastItemIndex)
val positionView = layoutManager.findViewByPosition(lastItemIndex)
lastVisibleTop = positionView?.top ?: 0
} else if (rememberPosition && tag != null) {
lastReadPositionKey = readStateManager.getPosition(tag)
lastVisibleTop = 0
} else {
lastReadPositionKey = -1
lastVisibleTop = 0
}
adapterData = data
val statusStartIndex = adapter.statusStartIndex
// The last status is statusEndExclusiveIndex - 1
val statusEndExclusiveIndex = statusStartIndex + adapter.statusCount
if (statusEndExclusiveIndex >= 0 && rememberPosition && tag != null) {
val lastPositionKey = adapter.getStatusPositionKey(statusEndExclusiveIndex - 1)
// Status corresponds to last read id was deleted, use last item id instead
if (lastPositionKey != -1L && lastReadPositionKey > 0 && lastReadPositionKey < lastPositionKey) {
lastReadPositionKey = lastPositionKey
val rememberPosition = preferences[rememberPositionKey]
val readPositionTag = currentReadPositionTag
val readFromBottom = preferences[readFromBottomKey]
val firstLoad = adapterData.isNullOrEmpty()
var lastReadId: Long = -1
var lastReadViewTop: Int = 0
var loadMore = false
// 1. Save current read position if not first load
if (!firstLoad) {
val lastVisibleItemPosition = layoutManager.findLastVisibleItemPosition()
val statusRange = rangeOfSize(adapter.statusStartIndex, adapter.statusCount - 1)
val lastReadPosition = if (readFromBottom) {
lastVisibleItemPosition
} else {
layoutManager.findFirstVisibleItemPosition()
}.coerceIn(statusRange)
lastReadId = if (useSortIdAsReadPosition) {
adapter.getStatusSortId(lastReadPosition)
} else {
adapter.getStatusPositionKey(lastReadPosition)
}
lastReadViewTop = layoutManager.findViewByPosition(lastReadPosition)?.top ?: 0
loadMore = lastVisibleItemPosition >= statusRange.endInclusive
} else if (rememberPosition && readPositionTag != null) {
lastReadId = readStateManager.getPosition(readPositionTag)
lastReadViewTop = 0
}
// 2. Change adapter data
adapterData = data
refreshEnabled = true
var restorePosition = -1
if (loader !is IExtendedLoader || loader.fromUser) {
if (hasMoreData(data)) {
adapter.loadMoreSupportedPosition = ILoadMoreSupportAdapter.END
@ -291,26 +308,23 @@ abstract class AbsStatusesFragment protected constructor() :
adapter.loadMoreSupportedPosition = ILoadMoreSupportAdapter.NONE
onHasMoreDataChanged(false)
}
var pos = -1
for (i in statusStartIndex..statusEndExclusiveIndex - 1) {
// Assume statuses are descend sorted by id, so break at first status with id
// lesser equals than read position
if (lastReadPositionKey != -1L && adapter.getStatusPositionKey(i) <= lastReadPositionKey) {
pos = i
break
}
}
if (pos != -1 && adapter.isStatus(pos) && (readFromBottom || lastVisiblePos != 0)) {
if (layoutManager.height == 0) {
// RecyclerView has not currently laid out, ignore padding.
layoutManager.scrollToPositionWithOffset(pos, lastVisibleTop)
} else {
layoutManager.scrollToPositionWithOffset(pos, lastVisibleTop - layoutManager.paddingTop)
}
restorePosition = if (useSortIdAsReadPosition) {
adapter.findPositionBySortId(lastReadId)
} else {
adapter.findPositionByPositionKey(lastReadId)
}
} else {
onHasMoreDataChanged(false)
}
if (restorePosition != -1 && adapter.isStatus(restorePosition) && (loadMore || readFromBottom)) {
if (layoutManager.height == 0) {
// RecyclerView has not currently laid out, ignore padding.
layoutManager.scrollToPositionWithOffset(restorePosition, lastReadViewTop)
} else {
layoutManager.scrollToPositionWithOffset(restorePosition, lastReadViewTop - layoutManager.paddingTop)
}
}
if (loader is IExtendedLoader) {
loader.fromUser = false
}

View File

@ -33,7 +33,9 @@ import org.mariotaku.twidere.view.TabPagerIndicator
/**
* Created by mariotaku on 16/3/16.
*/
abstract class AbsToolbarTabPagesFragment : BaseSupportFragment(), RefreshScrollTopInterface, SupportFragmentCallback, IBaseFragment.SystemWindowsInsetsCallback, ControlBarOffsetListener, HideUiOnScroll, OnPageChangeListener, IToolBarSupportFragment, KeyboardShortcutCallback {
abstract class AbsToolbarTabPagesFragment : BaseSupportFragment(), RefreshScrollTopInterface,
SupportFragmentCallback, IBaseFragment.SystemWindowsInsetsCallback, ControlBarOffsetListener,
HideUiOnScroll, OnPageChangeListener, IToolBarSupportFragment, KeyboardShortcutCallback {
private var pagerAdapter: SupportTabsAdapter? = null
override val toolbar: Toolbar

View File

@ -22,6 +22,7 @@ package org.mariotaku.twidere.fragment
import android.app.Activity
import android.content.Context
import android.content.Intent
import android.graphics.Rect
import android.media.RingtoneManager
import android.net.Uri
import android.os.Bundle
@ -29,15 +30,14 @@ import android.provider.Settings
import android.support.v7.preference.Preference
import android.support.v7.preference.PreferenceFragmentCompat
import org.mariotaku.kpreferences.KPreferences
import org.mariotaku.twidere.fragment.iface.IBaseFragment
import org.mariotaku.twidere.preference.RingtonePreference
import org.mariotaku.twidere.util.KeyboardShortcutsHandler
import org.mariotaku.twidere.util.UserColorNameManager
import org.mariotaku.twidere.util.dagger.GeneralComponentHelper
import javax.inject.Inject
abstract class BasePreferenceFragment : PreferenceFragmentCompat() {
abstract class BasePreferenceFragment : PreferenceFragmentCompat(), IBaseFragment {
private var ringtonePreferenceKey: String? = null
@Inject
@ -47,10 +47,7 @@ abstract class BasePreferenceFragment : PreferenceFragmentCompat() {
@Inject
lateinit var kPreferences: KPreferences
override fun onAttach(context: Context) {
super.onAttach(context)
GeneralComponentHelper.build(context).inject(this)
}
private val actionHelper = IBaseFragment.ActionHelper(this)
override fun onActivityCreated(savedInstanceState: Bundle?) {
if (savedInstanceState != null) {
@ -59,11 +56,21 @@ abstract class BasePreferenceFragment : PreferenceFragmentCompat() {
super.onActivityCreated(savedInstanceState)
}
override fun onSaveInstanceState(outState: Bundle?) {
outState!!.putString(EXTRA_RINGTONE_PREFERENCE_KEY, ringtonePreferenceKey)
override fun onAttach(context: Context) {
super.onAttach(context)
GeneralComponentHelper.build(context).inject(this)
}
override fun onSaveInstanceState(outState: Bundle) {
outState.putString(EXTRA_RINGTONE_PREFERENCE_KEY, ringtonePreferenceKey)
super.onSaveInstanceState(outState)
}
override fun onViewStateRestored(savedInstanceState: Bundle?) {
super.onViewStateRestored(savedInstanceState)
requestFitSystemWindows()
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
when (requestCode) {
REQUEST_PICK_RINGTONE -> {
@ -109,6 +116,14 @@ abstract class BasePreferenceFragment : PreferenceFragmentCompat() {
return super.onPreferenceTreeClick(preference)
}
override fun executeAfterFragmentResumed(action: (IBaseFragment) -> Unit) {
actionHelper.executeAfterFragmentResumed(action)
}
override fun fitSystemWindows(insets: Rect) {
listView.setPadding(insets.left, insets.top, insets.right, insets.bottom)
}
companion object {
private val REQUEST_PICK_RINGTONE = 301

View File

@ -20,12 +20,10 @@
package org.mariotaku.twidere.fragment
import android.content.Context
import android.graphics.Rect
import android.os.Bundle
import android.support.v4.app.Fragment
import android.support.v4.text.BidiFormatter
import com.squareup.otto.Bus
import org.mariotaku.twidere.constant.IntentConstants
import org.mariotaku.twidere.fragment.iface.IBaseFragment
import org.mariotaku.twidere.util.*
import org.mariotaku.twidere.util.dagger.GeneralComponentHelper
@ -71,40 +69,6 @@ open class BaseSupportFragment : Fragment(), IBaseFragment {
GeneralComponentHelper.build(context!!).inject(this)
}
override val extraConfiguration: Bundle?
get() {
return null
}
override val tabPosition: Int
get() {
val args = arguments ?: return -1
return args.getInt(IntentConstants.EXTRA_TAB_POSITION, -1)
}
override val tabId: Long
get() {
val args = arguments ?: return -1L
return args.getLong(IntentConstants.EXTRA_TAB_ID, -1L)
}
override fun requestFitSystemWindows() {
val activity = activity
val parentFragment = parentFragment
val callback: IBaseFragment.SystemWindowsInsetsCallback
if (parentFragment is IBaseFragment.SystemWindowsInsetsCallback) {
callback = parentFragment
} else if (activity is IBaseFragment.SystemWindowsInsetsCallback) {
callback = activity
} else {
return
}
val insets = Rect()
if (callback.getSystemWindowsInsets(insets)) {
fitSystemWindows(insets)
}
}
override fun executeAfterFragmentResumed(action: (IBaseFragment) -> Unit) {
actionHelper.executeAfterFragmentResumed(action)
}
@ -124,8 +88,4 @@ open class BaseSupportFragment : Fragment(), IBaseFragment {
DebugModeUtils.watchReferenceLeak(this)
}
protected open fun fitSystemWindows(insets: Rect) {
val view = view
view?.setPadding(insets.left, insets.top, insets.right, insets.bottom)
}
}

View File

@ -69,6 +69,8 @@ abstract class CursorStatusesFragment : AbsStatusesFragment() {
set(value) {
super.refreshing = value
}
override val useSortIdAsReadPosition: Boolean
get() = false
override fun onStatusesLoaded(loader: Loader<List<ParcelableStatus>?>, data: List<ParcelableStatus>?) {
showContentOrError()

View File

@ -0,0 +1,14 @@
package org.mariotaku.twidere.fragment
import android.os.Bundle
import org.mariotaku.twidere.Constants.SHARED_PREFERENCES_NAME
import org.mariotaku.twidere.R
class FilterSettingsFragment : BasePreferenceFragment() {
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
preferenceManager.sharedPreferencesName = SHARED_PREFERENCES_NAME
addPreferencesFromResource(R.xml.preferences_filters)
}
}

View File

@ -36,6 +36,8 @@ class FiltersFragment : AbsToolbarTabPagesFragment() {
adapter.addTab(cls = FilteredKeywordsFragment::class.java, name = getString(R.string.keywords))
adapter.addTab(cls = FilteredSourcesFragment::class.java, name = getString(R.string.sources))
adapter.addTab(cls = FilteredLinksFragment::class.java, name = getString(R.string.links))
adapter.addTab(cls = FilterSettingsFragment::class.java, name = getString(R.string.settings))
}
}

View File

@ -22,10 +22,7 @@ package org.mariotaku.twidere.fragment
import android.accounts.AccountManager
import android.app.Activity
import android.app.Dialog
import android.content.ContentValues
import android.content.Context
import android.content.DialogInterface
import android.content.Intent
import android.content.*
import android.graphics.Color
import android.graphics.Rect
import android.nfc.NdefMessage
@ -72,6 +69,7 @@ import kotlinx.android.synthetic.main.adapter_item_status_count_label.view.*
import kotlinx.android.synthetic.main.fragment_status.*
import kotlinx.android.synthetic.main.header_status_common.view.*
import kotlinx.android.synthetic.main.layout_content_fragment_common.*
import org.mariotaku.kpreferences.get
import org.mariotaku.ktextension.findPositionByItemId
import org.mariotaku.microblog.library.MicroBlogException
import org.mariotaku.microblog.library.twitter.model.Paging
@ -92,6 +90,7 @@ import org.mariotaku.twidere.annotation.AccountType
import org.mariotaku.twidere.annotation.Referral
import org.mariotaku.twidere.constant.KeyboardShortcutConstants.*
import org.mariotaku.twidere.constant.SharedPreferenceConstants
import org.mariotaku.twidere.constant.newDocumentApiKey
import org.mariotaku.twidere.extension.getAccountType
import org.mariotaku.twidere.loader.ConversationLoader
import org.mariotaku.twidere.loader.ParcelableStatusLoader
@ -1381,7 +1380,7 @@ class StatusFragment : BaseSupportFragment(), LoaderCallbacks<SingleResponse<Par
context: Context,
manager: MultiSelectManager,
private val adapter: StatusAdapter,
preferences: SharedPreferencesWrapper
preferences: SharedPreferences
) : StatusLinkClickHandler(context, manager, preferences) {
override fun onLinkClick(link: String, orig: String?, accountKey: UserKey?,
@ -1397,7 +1396,7 @@ class StatusFragment : BaseSupportFragment(), LoaderCallbacks<SingleResponse<Par
private fun expandOrOpenMedia(current: ParcelableMedia) {
if (adapter.isDetailMediaExpanded) {
IntentUtils.openMedia(adapter.context, adapter.status, current, null,
preferences.getBoolean(SharedPreferenceConstants.KEY_NEW_DOCUMENT_API))
preferences[newDocumentApiKey])
return
}
adapter.isDetailMediaExpanded = true

View File

@ -21,16 +21,42 @@ package org.mariotaku.twidere.fragment.iface
import android.graphics.Rect
import android.os.Bundle
import android.support.v4.app.Fragment
import org.mariotaku.twidere.constant.IntentConstants
import java.util.*
interface IBaseFragment {
val extraConfiguration: Bundle?
get() = null
val tabPosition: Int
get() = (this as Fragment).arguments?.getInt(IntentConstants.EXTRA_TAB_POSITION, -1) ?: -1
val tabId: Long
get() = (this as Fragment).arguments?.getLong(IntentConstants.EXTRA_TAB_ID, -1L) ?: -1L
fun requestFitSystemWindows()
fun requestFitSystemWindows() {
val fragment = this as Fragment
val activity = fragment.activity
val parentFragment = fragment.parentFragment
val callback: IBaseFragment.SystemWindowsInsetsCallback
if (parentFragment is IBaseFragment.SystemWindowsInsetsCallback) {
callback = parentFragment
} else if (activity is IBaseFragment.SystemWindowsInsetsCallback) {
callback = activity
} else {
return
}
val insets = Rect()
if (callback.getSystemWindowsInsets(insets)) {
fitSystemWindows(insets)
}
}
fun fitSystemWindows(insets: Rect) {
val fragment = this as Fragment
fragment.view?.setPadding(insets.left, insets.top, insets.right, insets.bottom)
}
interface SystemWindowsInsetsCallback {
fun getSystemWindowsInsets(insets: Rect): Boolean

View File

@ -42,6 +42,7 @@ import org.mariotaku.twidere.model.ParcelableStatus
import org.mariotaku.twidere.model.UserKey
import org.mariotaku.twidere.model.util.AccountUtils
import org.mariotaku.twidere.model.util.ParcelableStatusUtils
import org.mariotaku.twidere.task.twitter.GetStatusesTask
import org.mariotaku.twidere.util.JsonSerializer
import org.mariotaku.twidere.util.SharedPreferencesWrapper
import org.mariotaku.twidere.util.TwidereArrayUtils
@ -147,18 +148,26 @@ abstract class MicroBlogAPIStatusesLoader(
val noRowsDeleted = rowsDeleted == 0
val insertGap = minIdx != -1 && (noRowsDeleted || deletedOldGap) && !noItemsBefore
&& statuses.size >= loadItemLimit && !loadingMore
for (i in 0 until statuses.size) {
val status = statuses[i]
val item = ParcelableStatusUtils.fromStatus(status, accountKey, insertGap && isGapEnabled && minIdx == i)
ParcelableStatusUtils.updateExtraInformation(item, details, userColorNameManager)
data.add(item)
if (statuses.isNotEmpty()) {
val firstSortId = statuses.first().sortId
val lastSortId = statuses.last().sortId
// Get id diff of first and last item
val sortDiff = firstSortId - lastSortId
for (i in 0 until statuses.size) {
val status = statuses[i]
val item = ParcelableStatusUtils.fromStatus(status, accountKey, insertGap && isGapEnabled && minIdx == i)
item.position_key = GetStatusesTask.getPositionKey(item.timestamp, item.sort_id, lastSortId,
sortDiff, i, statuses.size)
ParcelableStatusUtils.updateExtraInformation(item, details, userColorNameManager)
data.add(item)
}
}
val db = TwidereApplication.getInstance(context).sqLiteDatabase
val array = data.toTypedArray()
var i = 0
val size = array.size
while (i < size) {
for (i in (0 until size)) {
val status = array[i]
val filtered = shouldFilterStatus(db, status)
if (filtered) {
@ -168,12 +177,12 @@ abstract class MicroBlogAPIStatusesLoader(
status.is_filtered = true
}
}
i++
}
if (comparator != null) {
Collections.sort(data, comparator)
data.sortWith(comparator!!)
} else {
Collections.sort(data)
data.sort()
}
saveCachedData(data)
return ListResponse.getListInstance(CopyOnWriteArrayList(data))

View File

@ -6,6 +6,8 @@ import android.text.style.URLSpan
import org.mariotaku.microblog.library.twitter.model.Status
import org.mariotaku.twidere.TwidereConstants.USER_TYPE_FANFOU_COM
import org.mariotaku.twidere.model.*
import org.mariotaku.twidere.model.ParcelableStatus.FilterFlags
import org.mariotaku.twidere.task.twitter.GetStatusesTask
import org.mariotaku.twidere.util.HtmlSpanBuilder
import org.mariotaku.twidere.util.InternalTwitterContentUtils
import org.mariotaku.twidere.util.TwitterContentUtils
@ -58,6 +60,13 @@ object ParcelableStatusUtils {
result.retweeted_by_user_profile_image = TwitterContentUtils.getProfileImageUrl(retweetUser)
result.extras.retweeted_external_url = retweetedStatus.inferExternalUrl()
if (retweetUser.isBlocking) {
result.filter_flags = result.filter_flags or FilterFlags.BLOCKING_USER
}
if (retweetUser.isBlockedBy) {
result.filter_flags = result.filter_flags or FilterFlags.BLOCKED_BY_USER
}
} else {
status = orig
}
@ -96,6 +105,8 @@ object ParcelableStatusUtils {
result.quoted_user_profile_image = TwitterContentUtils.getProfileImageUrl(quotedUser)
result.quoted_user_is_protected = quotedUser.isProtected
result.quoted_user_is_verified = quotedUser.isVerified
} else {
result.filter_flags = result.filter_flags or FilterFlags.QUOTE_NOT_AVAILABLE
}
result.reply_count = status.replyCount
@ -151,6 +162,7 @@ object ParcelableStatusUtils {
result.place_full_name = getPlaceFullName(status)
result.card_name = if (result.card != null) result.card!!.name else null
result.lang = status.lang
return result
}

View File

@ -0,0 +1,61 @@
package org.mariotaku.twidere.util
import android.widget.AbsListView
import org.mariotaku.twidere.util.support.ViewSupport
/**
* Created by mariotaku on 16/3/1.
*/
class ListViewScrollHandler(
contentListSupport: ContentScrollHandler.ContentListSupport,
viewCallback: ContentScrollHandler.ViewCallback?
) : ContentScrollHandler(contentListSupport, viewCallback), AbsListView.OnScrollListener,
ListScrollDistanceCalculator.ScrollDistanceListener {
private val calculator: ListScrollDistanceCalculator
var onScrollListener: AbsListView.OnScrollListener? = null
private var dy: Int = 0
private var oldState = AbsListView.OnScrollListener.SCROLL_STATE_IDLE
init {
calculator = ListScrollDistanceCalculator()
}
override fun onScrollStateChanged(view: AbsListView, scrollState: Int) {
calculator.onScrollStateChanged(view, scrollState)
calculator.setScrollDistanceListener(this)
handleScrollStateChanged(scrollState, AbsListView.OnScrollListener.SCROLL_STATE_IDLE)
if (onScrollListener != null) {
onScrollListener!!.onScrollStateChanged(view, scrollState)
}
}
override fun onScroll(view: AbsListView, firstVisibleItem: Int, visibleItemCount: Int, totalItemCount: Int) {
calculator.onScroll(view, firstVisibleItem, visibleItemCount, totalItemCount)
val scrollState = scrollState
handleScroll(dy, scrollState, oldState, AbsListView.OnScrollListener.SCROLL_STATE_IDLE)
if (onScrollListener != null) {
onScrollListener!!.onScroll(view, firstVisibleItem, visibleItemCount, totalItemCount)
}
}
val totalScrollDistance: Int
get() = calculator.totalScrollDistance
override fun onScrollDistanceChanged(delta: Int, total: Int) {
dy = -delta
val scrollState = scrollState
handleScroll(dy, scrollState, oldState, AbsListView.OnScrollListener.SCROLL_STATE_IDLE)
oldState = scrollState
}
class ListViewCallback(private val listView: AbsListView) : ContentScrollHandler.ViewCallback {
override val computingLayout: Boolean
get() = ViewSupport.isInLayout(listView)
override fun post(runnable: Runnable) {
listView.post(runnable)
}
}
}

View File

@ -22,6 +22,7 @@ package org.mariotaku.twidere.util
import android.content.ActivityNotFoundException
import android.content.Context
import android.content.Intent
import android.content.SharedPreferences
import android.net.Uri
import android.os.BadParcelableException
import android.support.customtabs.CustomTabsIntent
@ -30,11 +31,12 @@ import edu.tsinghua.hotmobi.model.LinkEvent
import org.apache.commons.lang3.StringUtils
import org.mariotaku.chameleon.Chameleon
import org.mariotaku.chameleon.ChameleonUtils
import org.mariotaku.kpreferences.get
import org.mariotaku.twidere.activity.WebLinkHandlerActivity
import org.mariotaku.twidere.annotation.Referral
import org.mariotaku.twidere.app.TwidereApplication
import org.mariotaku.twidere.constant.IntentConstants.EXTRA_ACCOUNT_KEY
import org.mariotaku.twidere.constant.SharedPreferenceConstants.KEY_NEW_DOCUMENT_API
import org.mariotaku.twidere.constant.newDocumentApiKey
import org.mariotaku.twidere.model.UserKey
import org.mariotaku.twidere.model.util.ParcelableMediaUtils
import org.mariotaku.twidere.util.TwidereLinkify.OnLinkClickListener
@ -43,7 +45,7 @@ import org.mariotaku.twidere.util.media.preview.PreviewMediaExtractor
open class OnLinkClickHandler(
protected val context: Context,
protected val manager: MultiSelectManager?,
protected val preferences: SharedPreferencesWrapper
protected val preferences: SharedPreferences
) : OnLinkClickListener {
override fun onLinkClick(link: String, orig: String?, accountKey: UserKey?,
@ -60,8 +62,7 @@ open class OnLinkClickHandler(
when (type) {
TwidereLinkify.LINK_TYPE_MENTION -> {
IntentUtils.openUserProfile(context, accountKey, null, link, null,
preferences.getBoolean(KEY_NEW_DOCUMENT_API),
Referral.USER_MENTION)
preferences[newDocumentApiKey], Referral.USER_MENTION)
return true
}
TwidereLinkify.LINK_TYPE_HASHTAG -> {
@ -101,7 +102,7 @@ open class OnLinkClickHandler(
}
val screenName = orig.substring(1, length)
IntentUtils.openUserProfile(context, accountKey, UserKey.valueOf(id),
screenName, null, preferences.getBoolean(KEY_NEW_DOCUMENT_API),
screenName, null, preferences[newDocumentApiKey],
Referral.USER_MENTION)
return true
}
@ -137,7 +138,7 @@ open class OnLinkClickHandler(
}
TwidereLinkify.LINK_TYPE_USER_ID -> {
IntentUtils.openUserProfile(context, accountKey, UserKey.valueOf(link), null, null,
preferences.getBoolean(KEY_NEW_DOCUMENT_API),
preferences[newDocumentApiKey],
Referral.USER_MENTION)
return true
}
@ -155,7 +156,7 @@ open class OnLinkClickHandler(
protected open fun openMedia(accountKey: UserKey, extraId: Long, sensitive: Boolean, link: String, start: Int, end: Int) {
val media = arrayOf(ParcelableMediaUtils.image(link))
IntentUtils.openMedia(context, accountKey, sensitive, null, media, null,
preferences.getBoolean(KEY_NEW_DOCUMENT_API))
preferences[newDocumentApiKey])
}
protected open fun openLink(link: String) {

View File

@ -21,9 +21,10 @@ package org.mariotaku.twidere.util
import android.content.Context
import android.support.v7.widget.RecyclerView
import org.mariotaku.kpreferences.get
import org.mariotaku.twidere.Constants
import org.mariotaku.twidere.adapter.iface.IStatusesAdapter
import org.mariotaku.twidere.constant.SharedPreferenceConstants.KEY_NEW_DOCUMENT_API
import org.mariotaku.twidere.constant.newDocumentApiKey
import org.mariotaku.twidere.model.UserKey
import org.mariotaku.twidere.model.util.ParcelableMediaUtils
@ -43,7 +44,7 @@ class StatusAdapterLinkClickHandler<D>(context: Context, preferences: SharedPref
if (current != null && current.open_browser) {
openLink(link)
} else {
val newDocument = preferences.getBoolean(KEY_NEW_DOCUMENT_API)
val newDocument = preferences[newDocumentApiKey]
IntentUtils.openMedia(context, status, current, null, newDocument)
}
}

View File

@ -20,8 +20,10 @@
package org.mariotaku.twidere.util
import android.content.Context
import android.content.SharedPreferences
import org.mariotaku.kpreferences.get
import org.mariotaku.twidere.Constants
import org.mariotaku.twidere.constant.SharedPreferenceConstants.KEY_NEW_DOCUMENT_API
import org.mariotaku.twidere.constant.newDocumentApiKey
import org.mariotaku.twidere.model.ParcelableMedia
import org.mariotaku.twidere.model.ParcelableStatus
import org.mariotaku.twidere.model.UserKey
@ -32,7 +34,7 @@ import org.mariotaku.twidere.model.UserKey
open class StatusLinkClickHandler(
context: Context,
manager: MultiSelectManager,
preferences: SharedPreferencesWrapper
preferences: SharedPreferences
) : OnLinkClickHandler(context, manager, preferences), Constants {
var status: ParcelableStatus? = null
@ -44,8 +46,7 @@ open class StatusLinkClickHandler(
if (current == null || current.open_browser) {
openLink(link)
} else {
IntentUtils.openMedia(context, status, current, null,
preferences.getBoolean(KEY_NEW_DOCUMENT_API))
IntentUtils.openMedia(context, status, current, null, preferences[newDocumentApiKey])
}
}

View File

@ -827,4 +827,5 @@
<string name="action_block">Block</string>
<string name="status_format_time_source"><xliff:g example="0:00, Jan 1 2017" id="time">%1$s</xliff:g> · <xliff:g example="Twidere for Android" id="source">%2$s</xliff:g></string>
<string name="status_format_source"><xliff:g example="Twidere for Android" id="source">%s</xliff:g></string>
<string name="preference_filter_unavailable_quote_statuses">Filter unavailable quotes</string>
</resources>

View File

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
<SwitchPreferenceCompat
android:defaultValue="false"
android:key="filter_unavailable_quote_statuses"
android:title="@string/preference_filter_unavailable_quote_statuses"/>
</PreferenceScreen>